diff --git a/.gitignore b/.gitignore index 8244f95..7e1cf4a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ build/ ### VS Code ### .vscode/ +*.log diff --git a/inventory-gov/src/main/java/com/rzyc/advice/InterceptAspect.java b/inventory-gov/src/main/java/com/rzyc/advice/InterceptAspect.java index fff559a..4c11848 100644 --- a/inventory-gov/src/main/java/com/rzyc/advice/InterceptAspect.java +++ b/inventory-gov/src/main/java/com/rzyc/advice/InterceptAspect.java @@ -3,6 +3,7 @@ package com.rzyc.advice; import com.alibaba.fastjson.JSONArray; import com.common.utils.model.MultiResult; import com.common.utils.model.SingleResult; +import com.rzyc.utils.IpUtil; import org.apache.commons.collections.map.HashedMap; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -40,7 +41,7 @@ public class InterceptAspect { String url = request.getRequestURL().toString(); System.out.println("url - - - - - - - > "+url); - System.out.println("ip -> "+request.getRemoteAddr()); + System.out.println("ip -> "+ IpUtil.getIpAddr(request)); Map requestMsg = new HashMap<>(); Map paramsMap = request.getParameterMap(); diff --git a/inventory-gov/src/main/java/com/rzyc/advice/LoginAspect.java b/inventory-gov/src/main/java/com/rzyc/advice/LoginAspect.java index 5adcd52..e283e99 100644 --- a/inventory-gov/src/main/java/com/rzyc/advice/LoginAspect.java +++ b/inventory-gov/src/main/java/com/rzyc/advice/LoginAspect.java @@ -2,8 +2,8 @@ package com.rzyc.advice; import com.common.utils.StringUtils; import com.common.utils.jwt.JwtUtil; +import com.rzyc.advice.exception.TokenException; import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; diff --git a/inventory-gov/src/main/java/com/rzyc/advice/exception/AccessException.java b/inventory-gov/src/main/java/com/rzyc/advice/exception/AccessException.java new file mode 100644 index 0000000..157271f --- /dev/null +++ b/inventory-gov/src/main/java/com/rzyc/advice/exception/AccessException.java @@ -0,0 +1,21 @@ +package com.rzyc.advice.exception; + +/** + * ip限制异常 + * @author dong + * @date 2023-11-22 14:45 + * @Version V1.0 + */ +public class AccessException extends RuntimeException{ + + public AccessException() { + super(); + // TODO 自动生成的构造函数存根 + } + public AccessException(String message) { + super(message); +// System.out.println(message); + // TODO 自动生成的构造函数存根 + } + +} diff --git a/inventory-gov/src/main/java/com/rzyc/advice/ExceptionAdvice.java b/inventory-gov/src/main/java/com/rzyc/advice/exception/ExceptionAdvice.java similarity index 92% rename from inventory-gov/src/main/java/com/rzyc/advice/ExceptionAdvice.java rename to inventory-gov/src/main/java/com/rzyc/advice/exception/ExceptionAdvice.java index ac6b066..4e1ced9 100644 --- a/inventory-gov/src/main/java/com/rzyc/advice/ExceptionAdvice.java +++ b/inventory-gov/src/main/java/com/rzyc/advice/exception/ExceptionAdvice.java @@ -1,4 +1,4 @@ -package com.rzyc.advice; +package com.rzyc.advice.exception; import com.common.utils.model.Code; import com.common.utils.model.Message; @@ -127,7 +127,7 @@ public class ExceptionAdvice { * token授权处理 * @return */ - @ExceptionHandler(com.rzyc.advice.TokenException.class) + @ExceptionHandler(TokenException.class) public SingleResult handleTokenException(Exception e){ e.printStackTrace(); logger.info("TokenException message -> "+e.getMessage()); @@ -137,6 +137,20 @@ public class ExceptionAdvice { return result; } + /** + * ip访问限制 + * @return + */ + @ExceptionHandler(AccessException.class) + public SingleResult handleAccessException(Exception e){ + e.printStackTrace(); + logger.info("TokenException message -> "+e.getMessage()); + SingleResult result = new SingleResult<>(); + result.setCode(Code.ERROR.getCode()); + result.setMessage(e.getMessage()); + return result; + } + /** * 通一异常处理 * @return diff --git a/inventory-gov/src/main/java/com/rzyc/advice/TokenException.java b/inventory-gov/src/main/java/com/rzyc/advice/exception/TokenException.java similarity index 90% rename from inventory-gov/src/main/java/com/rzyc/advice/TokenException.java rename to inventory-gov/src/main/java/com/rzyc/advice/exception/TokenException.java index 836ba72..035c5bd 100644 --- a/inventory-gov/src/main/java/com/rzyc/advice/TokenException.java +++ b/inventory-gov/src/main/java/com/rzyc/advice/exception/TokenException.java @@ -1,4 +1,4 @@ -package com.rzyc.advice; +package com.rzyc.advice.exception; /** * token exception diff --git a/inventory-gov/src/main/java/com/rzyc/advice/log/LogAspect.java b/inventory-gov/src/main/java/com/rzyc/advice/log/LogAspect.java index 672599c..72de48d 100644 --- a/inventory-gov/src/main/java/com/rzyc/advice/log/LogAspect.java +++ b/inventory-gov/src/main/java/com/rzyc/advice/log/LogAspect.java @@ -1,23 +1,30 @@ package com.rzyc.advice.log; +import cn.hutool.extra.spring.SpringUtil; import com.alibaba.fastjson.JSONArray; import com.common.utils.RandomNumber; import com.common.utils.StringUtils; import com.common.utils.jwt.JwtUtil; import com.common.utils.model.Result; +import com.rzyc.advice.exception.AccessException; +import com.rzyc.config.RedisUtil; import com.rzyc.mapper.log.SysLogsMapper; import com.rzyc.mapper.user.SysUserMapper; import com.rzyc.model.user.SysUser; import com.rzyc.model.log.SysLogsWithBLOBs; +import com.rzyc.utils.IpUtil; import org.apache.commons.collections.map.HashedMap; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -26,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; /** * 日志记录 @@ -45,6 +53,21 @@ public class LogAspect { @Autowired protected SysUserMapper sysUserMapper; + + //单位时间内最大访问数 + private static final Integer MAX_COUNT = 20; + + //单位时间 + private static final Integer UNIT_TIME = 1 * 1000; + + //限制时长 + private static final Long REJECT_TIME = 10 * 60 * 1000L; + + @Autowired + private RedisUtil redisUtil; + + private ApplicationContext context; + /** * 拦截位置 */ @@ -57,14 +80,28 @@ public class LogAspect { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder .getRequestAttributes()).getRequest(); + + String ip = IpUtil.getIpAddr(request); System.out.println(""); - System.out.println("uri -> "+request.getRequestURI()); - System.out.println("ip -> "+request.getRemoteAddr()); + System.out.println("uri -> "+ request.getRequestURI()); + System.out.println("ip -> "+ip); + + + + /** + * 先判断ip访问是否超过限制 在记录日志 + * @version v1.0 + * @author dong + * @date 2023/11/22 14:57 + */ + //判断ip访问数量 + assessIp(ip); + + Map requestMsg = new HashMap<>(); String queryString = request.getQueryString(); System.out.println("queryString -> "+queryString); -// System.out.println("joinPoint -> "+JSONArray.toJSONString(joinPoint.getArgs())); //获取存在header中的用户token @@ -113,7 +150,7 @@ public class LogAspect { logs.setUserId(userId); logs.setParams(JSONArray.toJSONString(requestMsg)); logs.setResponseStr(JSONArray.toJSONString(responseMap)); - logs.setIpAddress(request.getRemoteAddr()); + logs.setIpAddress(ip); logs.setUrl(request.getRequestURI()); saveLog saveLog = new saveLog(logs); Thread thread = new Thread(saveLog); @@ -122,6 +159,51 @@ public class LogAspect { return obj; } + /** + * 通过ip判断访问数量限制 + * @version v1.0 + * @author dong + * @date 2023/11/22 14:58 + */ + private void assessIp(String ip)throws Exception{ + //过滤黑名单 + if (redisUtil.hasKey("filter:ip:black:" + ip)) { + System.out.println("ip访问过于频繁,已被限制=>" + ip + " 倒计时" + redisUtil.getExpire("filter:ip:black:" + ip)); + throw new AccessException("访问过于频繁,请稍后重试"); + } + //判断ip是否首次访问 + if (redisUtil.hExists("filter:ip:normal", "count:" + ip)) { + //判断最大访问次数 + Integer maxCount = Integer.valueOf(redisUtil.hGet("filter:ip:normal", "count:" + ip).toString()); + System.out.println("ip:" + ip + " 访问" + maxCount + "次"); + if (maxCount > MAX_COUNT) { + Long maxTime = Long.valueOf(redisUtil.hGet("filter:ip:normal", "time:" + ip).toString()); + if (System.currentTimeMillis() - maxTime < UNIT_TIME) { + System.out.println("ip访问过于频繁,已被限制=>" + ip + " 倒计时" + REJECT_TIME); + redisUtil.setEx("filter:ip:black:" + ip, "1", REJECT_TIME, TimeUnit.MILLISECONDS); + String str[] = {"count:" + ip, "time:" + ip}; + redisUtil.hDelete("filter:ip:normal", str); + throw new AccessException("访问过于频繁,请稍后重试"); + } + initVisitsIP(ip); + } + } else { + System.out.println("--------->initVisitsIP"); + initVisitsIP(ip); + } + redisUtil.hIncrBy("filter:ip:normal", "count:" + ip, 1); + } + + /** + * 初始化访问ip + * @param ip + */ + private void initVisitsIP(String ip) { + redisUtil.hPut("filter:ip:normal", "count:" + ip, "0"); + redisUtil.hPut("filter:ip:normal", "time:" + ip, String.valueOf(System.currentTimeMillis())); + } + + /** * 异步保存日志 */ diff --git a/inventory-gov/src/main/java/com/rzyc/config/RedisUtil.java b/inventory-gov/src/main/java/com/rzyc/config/RedisUtil.java index 31d7fb6..5ee26c9 100644 --- a/inventory-gov/src/main/java/com/rzyc/config/RedisUtil.java +++ b/inventory-gov/src/main/java/com/rzyc/config/RedisUtil.java @@ -3,6 +3,7 @@ package com.rzyc.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -17,6 +18,95 @@ public class RedisUtil { @Autowired RedisTemplate redisTemplate; + @Autowired + private StringRedisTemplate redisTemp; + + public void setRedisTemplate(StringRedisTemplate redisTemp) { + this.redisTemp = redisTemp; + } + + + /** + * 查看哈希表 key 中,指定的字段是否存在 + * + * @param key + * @param field + * @return + */ + public boolean hExists(String key, String field) { + return redisTemp.opsForHash().hasKey(key, field); + } + + + /** + * 获取存储在哈希表中指定字段的值 + * + * @param key + * @param field + * @return + */ + public Object hGet(String key, String field) { + return redisTemp.opsForHash().get(key, field); + } + + + /** + * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout + * + * @param key + * @param value + * @param timeout 过期时间 + * @param unit 时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES + * 秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS + */ + public void setEx(String key, String value, long timeout, TimeUnit unit) { + redisTemp.opsForValue().set(key, value, timeout, unit); + } + + /** + * 删除一个或多个哈希表字段 + * + * @param key + * @param fields + * @return + */ + public Long hDelete(String key, Object... fields) { + return redisTemp.opsForHash().delete(key, fields); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param increment + * @return + */ + public Long hIncrBy(String key, Object field, long increment) { + return redisTemp.opsForHash().increment(key, field, increment); + } + + /** + * 写入数据 + * @version v1.0 + * @author dong + * @date 2023/11/22 14:04 + */ + public void hPut(String key, String hashKey, String value) { + redisTemp.opsForHash().put(key, hashKey, value); + } + + /** + * 是否存在key + * + * @param key + * @return + */ + public Boolean hasKey(String key) { + return redisTemp.hasKey(key); + } + + /** * get 多参数分隔 : * @param key 键 @@ -63,19 +153,6 @@ public class RedisUtil { } - /** - * 判断key是否存在 - * @param key 键 - * @return true 存在 false不存在 - */ - public boolean hasKey(String key) { - try { - return redisTemplate.hasKey(key); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } /** diff --git a/inventory-gov/src/main/java/com/rzyc/filter/IpFilter.java b/inventory-gov/src/main/java/com/rzyc/filter/IpFilter.java new file mode 100644 index 0000000..e7fca5e --- /dev/null +++ b/inventory-gov/src/main/java/com/rzyc/filter/IpFilter.java @@ -0,0 +1,119 @@ +package com.rzyc.filter; + +import com.rzyc.config.RedisUtil; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * @author dong + * @date 2023-11-22 13:55 + * @Version V1.0 + */ +//@Order(0) +//@Component +//@WebFilter(filterName = "IPFilter", urlPatterns = "/*") +public class IpFilter implements Filter { + + + //单位时间内最大访问数 + private static final Integer MAX_COUNT = 15; + + //单位时间 + private static final Integer UNIT_TIME = 1 * 1000; + + //限制时长 + private static final Long REJECT_TIME = 10 * 60 * 1000L; + + private static RedisUtil redisUtil; + + private ApplicationContext context; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + context = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext()); + if (redisUtil == null) { + redisUtil = new RedisUtil(); + StringRedisTemplate redisTemplate = (StringRedisTemplate) context.getBean("stringRedisTemplate"); + redisUtil.setRedisTemplate(redisTemplate); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + String ip = request.getRemoteAddr(); + //过滤黑名单 + if (redisUtil.hasKey("filter:ip:black:" + ip)) { + System.out.println("ip访问过于频繁,已被限制=>" + ip + " 倒计时" + redisUtil.getExpire("filter:ip:black:" + ip)); + return; + } + //判断ip是否首次访问 + if (redisUtil.hExists("filter:ip:normal", "count:" + ip)) { + //判断最大访问次数 + Integer maxCount = Integer.valueOf(redisUtil.hGet("filter:ip:normal", "count:" + ip).toString()); + System.out.println("ip:" + ip + " 访问" + maxCount + "次"); + if (maxCount > MAX_COUNT) { + Long maxTime = Long.valueOf(redisUtil.hGet("filter:ip:normal", "time:" + ip).toString()); + if (System.currentTimeMillis() - maxTime < UNIT_TIME) { + System.out.println("ip访问过于频繁,已被限制=>" + ip + " 倒计时" + REJECT_TIME); + redisUtil.setEx("filter:ip:black:" + ip, "1", REJECT_TIME, TimeUnit.MILLISECONDS); + String str[] = {"count:" + ip, "time:" + ip}; + redisUtil.hDelete("filter:ip:normal", str); + return; + } + initVisitsIP(ip); + } + } else { + initVisitsIP(ip); + } + chain.doFilter(request, response); + redisUtil.hIncrBy("filter:ip:normal", "count:" + ip, 1); + } + + /** + * 初始化访问ip + * @param ip + */ + private void initVisitsIP(String ip) { + redisUtil.hPut("filter:ip:normal", "count:" + ip, "0"); + redisUtil.hPut("filter:ip:normal", "time:" + ip, String.valueOf(System.currentTimeMillis())); + } + + @Override + public void destroy() { + } + + /** + * 获取真实ip + * @author: hanguodong + * @date: 2023/6/6 22:12 + * @param: [request] + * @return: + **/ + public String getIpAddr(HttpServletRequest request) { + String ip = request.getHeader("X-Real-IP"); + if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) { + return ip; + } + ip = request.getHeader("X-Forwarded-For"); + if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) { + int index = ip.indexOf(','); + if (index != -1) { + return ip.substring(0, index); + } else { + return ip; + } + } else { + return request.getRemoteAddr(); + } + } + +} diff --git a/inventory-gov/src/main/java/com/rzyc/filter/JwtAuthenticationTokenFiler.java b/inventory-gov/src/main/java/com/rzyc/filter/JwtAuthenticationTokenFiler.java index a991383..9c0a3fc 100644 --- a/inventory-gov/src/main/java/com/rzyc/filter/JwtAuthenticationTokenFiler.java +++ b/inventory-gov/src/main/java/com/rzyc/filter/JwtAuthenticationTokenFiler.java @@ -4,6 +4,7 @@ import com.common.utils.jwt.JwtUtil; import com.rzyc.advice.CustomException; import com.rzyc.mapper.AuthorityKeyMapper; import com.rzyc.model.AuthorityKey; +import com.rzyc.utils.IpUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.AccessDeniedException; @@ -40,7 +41,8 @@ public class JwtAuthenticationTokenFiler extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //获取token String token = request.getHeader("userToken"); - System.out.println("addr ------> "+request.getLocalAddr()); + + System.out.println("addr ------> "+ IpUtil.getIpAddr(request)); /* if (!StringUtils.hasText(token)) { //放行 filterChain.doFilter(request, response); diff --git a/inventory-gov/src/main/java/com/rzyc/utils/IpUtil.java b/inventory-gov/src/main/java/com/rzyc/utils/IpUtil.java new file mode 100644 index 0000000..09b4aa3 --- /dev/null +++ b/inventory-gov/src/main/java/com/rzyc/utils/IpUtil.java @@ -0,0 +1,37 @@ +package com.rzyc.utils; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author dong + * @date 2023-11-22 14:32 + * @Version V1.0 + */ +public class IpUtil { + + /** + * 获取真实ip + * @author: hanguodong + * @date: 2023/6/6 22:12 + * @param: [request] + * @return: + **/ + public static String getIpAddr(HttpServletRequest request) { + String ip = request.getHeader("X-Real-IP"); + if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) { + return ip; + } + ip = request.getHeader("X-Forwarded-For"); + if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) { + int index = ip.indexOf(','); + if (index != -1) { + return ip.substring(0, index); + } else { + return ip; + } + } else { + return request.getRemoteAddr(); + } + } + +} diff --git a/inventory-gov/src/main/java/com/rzyc/utils/RedisUtil.java b/inventory-gov/src/main/java/com/rzyc/utils/RedisUtil.java deleted file mode 100644 index 52d908a..0000000 --- a/inventory-gov/src/main/java/com/rzyc/utils/RedisUtil.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.rzyc.utils; - -/** - * redis 方法 - * @author dong - * @date 2023-11-22 10:53 - * @Version V1.0 - */ -public class RedisUtil { - - public static void main(String[] args) { - System.out.println(""); - System.out.println(""); - System.out.println(""); - } - -} diff --git a/inventory-gov/src/main/resources/application-test.yml b/inventory-gov/src/main/resources/application-test.yml index ad92695..22621cf 100644 --- a/inventory-gov/src/main/resources/application-test.yml +++ b/inventory-gov/src/main/resources/application-test.yml @@ -2,6 +2,17 @@ server: port: 7010 spring: + redis: + host: 42.193.40.239 + password: zysoft2023@com + port: 6937 + lettuce: + pool: + max-active: 8 + max-idle: 8 + min-idle: 0 + max-wait: 100 + shutdown-timeout: 50000 servlet: multipart: enabled: true @@ -59,9 +70,9 @@ logging: #文件上传的配置 onstants: #上传文件存放地址 - file_location: /static/resource/inventory/uploadFile/ + file_location: C:/mnt/resource/inventory/uploadFile/ #上传文件公共地址 - file_header: /static/resource/inventory/uploadFile/ + file_header: /resource/inventory/uploadFile/ #上传文件基础路径 service_file_header: http://172.27.181.247:8010/ #父级地区id