Browse Source

新增接口限制过滤器

xusl 2 years ago
parent
commit
45058ebab2

+ 2 - 24
backend/src/main/java/com/jiayue/ssi/aspectj/InterfaceLimitAspect.java

@@ -1,11 +1,8 @@
 package com.jiayue.ssi.aspectj;
 package com.jiayue.ssi.aspectj;
 
 
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
-
 import com.jiayue.ssi.annotation.InterfaceLimit;
 import com.jiayue.ssi.annotation.InterfaceLimit;
+import com.jiayue.ssi.util.InterfaceLimitUtil;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Aspect;
@@ -14,12 +11,8 @@ import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 import org.springframework.web.context.request.ServletRequestAttributes;
-import com.jiayue.ssi.util.IPUtils;
 import com.jiayue.ssi.util.ResponseVO;
 import com.jiayue.ssi.util.ResponseVO;
-
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
-import net.jodah.expiringmap.ExpirationPolicy;
-import net.jodah.expiringmap.ExpiringMap;
 
 
 /**
 /**
  * 接口限制实现
  * 接口限制实现
@@ -31,8 +24,6 @@ import net.jodah.expiringmap.ExpiringMap;
 @Component
 @Component
 @Slf4j
 @Slf4j
 public class InterfaceLimitAspect {
 public class InterfaceLimitAspect {
-    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();
-
     /**
     /**
      * 层切点
      * 层切点
      */
      */
@@ -46,25 +37,12 @@ public class InterfaceLimitAspect {
         RequestAttributes ra = RequestContextHolder.getRequestAttributes();
         RequestAttributes ra = RequestContextHolder.getRequestAttributes();
         ServletRequestAttributes sra = (ServletRequestAttributes) ra;
         ServletRequestAttributes sra = (ServletRequestAttributes) ra;
         HttpServletRequest request = sra.getRequest();
         HttpServletRequest request = sra.getRequest();
-
-        // 获取Map value对象, 如果没有则返回默认值
-        // getOrDefault获取参数,获取不到则给默认值
-        ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
-        Integer uCount = uc.getOrDefault(IPUtils.getIpAddr(request), 0);
-        if (uCount >= interfaceLimit.value()) { // 超过次数,不执行目标方法
+        if (!InterfaceLimitUtil.checkInterface(request,interfaceLimit.time(),interfaceLimit.value())){
             log.error("接口拦截:{} 请求超过限制频率【{}次/{}ms】,IP为{}", request.getRequestURI(), interfaceLimit.value(), interfaceLimit.time(), request.getRemoteAddr());
             log.error("接口拦截:{} 请求超过限制频率【{}次/{}ms】,IP为{}", request.getRequestURI(), interfaceLimit.value(), interfaceLimit.time(), request.getRemoteAddr());
             return ResponseVO.fail(null,"请求过于频繁,请稍后再试");
             return ResponseVO.fail(null,"请求过于频繁,请稍后再试");
-        } else if (uCount == 0) { // 第一次请求时,设置有效时间
-            uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, interfaceLimit.time(), TimeUnit.MILLISECONDS);
-        } else { // 未超过次数, 记录加一
-            uc.put(request.getRemoteAddr(), uCount + 1);
         }
         }
-        book.put(request.getRequestURI(), uc);
-
         // result的值就是被拦截方法的返回值
         // result的值就是被拦截方法的返回值
         ResponseVO result = (ResponseVO)pjp.proceed();
         ResponseVO result = (ResponseVO)pjp.proceed();
-
         return result;
         return result;
     }
     }
-
 }
 }

+ 2 - 0
backend/src/main/java/com/jiayue/ssi/config/WebSecurityConfig.java

@@ -1,5 +1,6 @@
 package com.jiayue.ssi.config;
 package com.jiayue.ssi.config;
 
 
+import com.jiayue.ssi.filter.InterfaceLimitFilter;
 import com.jiayue.ssi.filter.JwtAuthenticationTokenFilter;
 import com.jiayue.ssi.filter.JwtAuthenticationTokenFilter;
 import com.jiayue.ssi.filter.MailCodeFilter;
 import com.jiayue.ssi.filter.MailCodeFilter;
 import com.jiayue.ssi.filter.VerifyCodeFilter;
 import com.jiayue.ssi.filter.VerifyCodeFilter;
@@ -58,6 +59,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
 
     @Override
     @Override
     protected void configure(HttpSecurity httpSecurity) throws Exception {
     protected void configure(HttpSecurity httpSecurity) throws Exception {
+        httpSecurity.addFilterBefore(new InterfaceLimitFilter(), UsernamePasswordAuthenticationFilter.class);
         httpSecurity.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);
         httpSecurity.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);
         httpSecurity.addFilterBefore(new MailCodeFilter(), UsernamePasswordAuthenticationFilter.class);
         httpSecurity.addFilterBefore(new MailCodeFilter(), UsernamePasswordAuthenticationFilter.class);
         httpSecurity.addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
         httpSecurity.addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

+ 39 - 0
backend/src/main/java/com/jiayue/ssi/filter/InterfaceLimitFilter.java

@@ -0,0 +1,39 @@
+package com.jiayue.ssi.filter;
+
+import com.jiayue.ssi.util.InterfaceLimitUtil;
+import com.jiayue.ssi.util.ResponseVO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 接口访问限制过滤器
+ *
+ * @author xsl
+ * @since 2023/02/24
+ */
+@RequiredArgsConstructor
+@Order(1)
+@Slf4j
+public class InterfaceLimitFilter extends OncePerRequestFilter {
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+        throws ServletException, IOException {
+        if (!InterfaceLimitUtil.checkInterface(request, 1000, 1)) {
+            log.error("接口拦截:{} 请求超过限制频率【{}次/{}ms】,IP为{}", request.getRequestURI(), 1000, 1, request.getRemoteAddr());
+            response.setHeader("Access-Control-Allow-Origin", "*");
+            response.setStatus(401);
+            response.setContentType("text/html;charset=utf-8");
+            response.getWriter().write("请求过于频繁,请稍后再试!");
+            return;
+        }
+        filterChain.doFilter(request, response);
+    }
+}

+ 1 - 1
backend/src/main/java/com/jiayue/ssi/filter/MailCodeFilter.java

@@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor;
 * @since 2023/02/20
 * @since 2023/02/20
 */
 */
 @RequiredArgsConstructor
 @RequiredArgsConstructor
-@Order(2)
+@Order(3)
 public class MailCodeFilter extends OncePerRequestFilter {
 public class MailCodeFilter extends OncePerRequestFilter {
     private String defaultFilterProcessUrl = "/user/login";
     private String defaultFilterProcessUrl = "/user/login";
 
 

+ 1 - 1
backend/src/main/java/com/jiayue/ssi/filter/VerifyCodeFilter.java

@@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor;
 * @since 2023/02/20
 * @since 2023/02/20
 */
 */
 @RequiredArgsConstructor
 @RequiredArgsConstructor
-@Order(1)
+@Order(2)
 public class VerifyCodeFilter extends OncePerRequestFilter {
 public class VerifyCodeFilter extends OncePerRequestFilter {
     private String defaultFilterProcessUrl = "/user/login";
     private String defaultFilterProcessUrl = "/user/login";
 
 

+ 39 - 0
backend/src/main/java/com/jiayue/ssi/util/InterfaceLimitUtil.java

@@ -0,0 +1,39 @@
+package com.jiayue.ssi.util;
+
+import net.jodah.expiringmap.ExpirationPolicy;
+import net.jodah.expiringmap.ExpiringMap;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+* 接口访问限制工具类
+*
+* @author xsl
+* @since 2023/02/24
+*/
+public class InterfaceLimitUtil {
+    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> interfaceLimitMap = new ConcurrentHashMap<>();
+
+    /**
+     * 接口检测*
+     * @param request
+     * @param time 限制时间 单位:毫秒
+     * @param value 允许请求的次数
+     * @return boolean true:允许通过 false:不通过
+     */
+    public static boolean checkInterface(HttpServletRequest request,long time,int value){
+        ExpiringMap<String, Integer> uc = interfaceLimitMap.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
+        Integer uCount = uc.getOrDefault(IPUtils.getIpAddr(request), 0);
+        if (uCount >= value) { // 超过次数,不执行目标方法
+            return false;
+        } else if (uCount == 0) { // 第一次请求时,设置有效时间
+            uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, time, TimeUnit.MILLISECONDS);
+        } else { // 未超过次数, 记录加一
+            uc.put(request.getRemoteAddr(), uCount + 1);
+        }
+        interfaceLimitMap.put(request.getRequestURI(), uc);
+        return true;
+    }
+}