Java自定义注解-请求限流防刷

Java自定义注解-请求限流防刷,第1张

兄弟们,相信遇到过重复请求的痛点吧,我也遇过,因此,写了一个自定义注解去解决这个问题,接下来看代码。

首先:创建一个自定义注解 RequestLimit 。然后字段的话是 second、maxCount。看代码

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Description 请求限流/防刷自定义注解
 * @date 2022/04/05 18:20
 * @author by TheSky
 */
@Documented
@Inherited
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
    // 在 second 秒内,最大只能请求 maxCount 次
    int second() default 1;
    int maxCount() default 1;

}

接下来就是拦截器了,创建拦截器: RequestLimitIntercept  继承 HandlerInterceptorAdapter

看代码:

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Description 请求限流/防刷拦截器
 * @date 2022/04/05 18:41
 * @author by TheSky
 */
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
         * isAssignableFrom()方法是判断是否为某个类的父类
         * instanceof关键字是判断是否某个类的子类
         */
        if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            //HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            // 获取方法中是否包含注解
            RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
            //获取 类中是否包含注解,也就是controller 是否有注解
            RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
            // 如果 方法上有注解就优先选择方法上的参数,否则类上的参数
            RequestLimit requestLimit = methodAnnotation != null ? methodAnnotation : classAnnotation;
            if (requestLimit != null) {
                if (isLimit(request, requestLimit)) {
                    resonseOut(response, new MyResult().faild("请求频繁,请稍后再试!"));
                    return false;
                }
            }
        }
        return super.preHandle(request, response, handler);
    }

    //判断请求是否受限
    public boolean isLimit(HttpServletRequest request, RequestLimit requestLimit) {
        // 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
        String UUID = request.getHeader("UUID");
        if (StringUtils.isBlank(UUID)) {
            log.warn("缺少唯一标识!");
            return false;
        }
        String limitKey = request.getServletPath() + UUID;
        // 从缓存中获取,当前这个请求访问了几次
        String redisCount = (String) RedisUtil.get(limitKey);
        if (redisCount == null || StringUtils.equalsIgnoreCase(redisCount, "null")) {
            //初始次数及过期时间;将值设置进redis,过期时间的话就是上面的自定义注解的秒数
            RedisUtil.set(limitKey, "1", requestLimit.second(), TimeUnit.SECONDS);
        } else {
            Integer count = Integer.valueOf(redisCount);
            if (count >= requestLimit.maxCount()) {
                return true;
            }
            // 次数自增
            RedisUtil.increment(limitKey);
        }
        return false;
    }

    /**
     * 回写给客户端
     *
     * @param response
     * @param result
     * @throws IOException
     */
    private void resonseOut(HttpServletResponse response, MyResult result) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        String json = JSONObject.toJSON(result).toString();
        out = response.getWriter();
        out.append(json);
    }

}

接下来呢,就是将这个拦截器给实现起来。

创建 WebConfig 实现  WebMvcConfigurer。看代码:


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Description 请求限流/防刷 实现
 * @date 2022/04/05 19:30
 * @author by TheSky
 */
@Configuration
@Slf4j
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private RequestLimitIntercept requestLimitIntercept;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("添加拦截");
        registry.addInterceptor(requestLimitIntercept);
    }
}

至此该注解 @RequestLimit  就可以被使用起来啦!

举个荔枝吧:

    //在Controller的接口中就能使用啦

    @GetMapping("/test")
    @RequestLimit(maxCount = 1, second = 5)
    public MyResult test(String t1, String t2) {
        return new MyResult().ok();
    }

至此,功能结束!有什么问题请多指教哈!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/790418.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存