通过注解实现接口限流防刷

Posted moris5013

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过注解实现接口限流防刷相关的知识,希望对你有一定的参考价值。

采用注解的方式

1)定义一个注解

@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
    int seconds();
    int maxCount();
}

2)在需要限流的方法前加这样的注解

@AccessLimit(seconds=5, maxCount=5)

3)在拦截器里进行判断,看方法是否使用了AccessLimit注解修饰

@Component
public class AccessInterceptor  extends HandlerInterceptorAdapter{
    
    @Autowired
    MiaoshaUserService miaoshaUserService;
    
    @Autowired
    RedisService redisService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("prehandle....");
        if(handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod)handler;
            String methodName = hm.getMethod().getName();
            if(methodName.equals("toLogin") || methodName.equals("do_login")){
                return true;
            }else{
                //获取用户登录信息放入线程上下文中
                MiaoshaUser user = getUser(request, response);
                UserContext.setUser(user);

                //判断防刷限流注解
                AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
                if(accessLimit == null) {
                    return true;
                }
                int seconds = accessLimit.seconds();
                int maxCount = accessLimit.maxCount();
                String key = request.getRequestURI();
                AccessKey ak = AccessKey.withExpire(seconds);
                Integer count = redisService.get(ak, key, Integer.class);
                if(count  == null) {
                    redisService.set(ak, key, 1);
                }else if(count < maxCount) {
                    redisService.incr(ak, key);
                }else {
                    render(response, CodeMsg.ACCESS_LIMIT_REACHED);
                    return false;
                }
            }
        }
        return true;
    }
    
    private void render(HttpServletResponse response, CodeMsg cm)throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = JSON.toJSONString(Result.error(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }

    private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response) {
        String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
        String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
        if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            throw new GlobalException(CodeMsg.SESSION_ERROR);
        }
        String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
     //这里是从redis中取用户信息 MiaoshaUser miaoshaUser
= miaoshaUserService.getByToken(response, token); if(null == miaoshaUser){ throw new GlobalException(CodeMsg.SESSION_ERROR); } return miaoshaUser; } private String getCookieValue(HttpServletRequest request, String cookiName) { Cookie[] cookies = request.getCookies(); if(cookies == null || cookies.length <= 0){ return null; } for(Cookie cookie : cookies) { if(cookie.getName().equals(cookiName)) { return cookie.getValue(); } } return null; } }
public class UserContext {
    
    private static ThreadLocal<MiaoshaUser> userHolder = new ThreadLocal<MiaoshaUser>();
    
    public static void setUser(MiaoshaUser user) {
        userHolder.set(user);
    }
    
    public static MiaoshaUser getUser() {
        return userHolder.get();
    }

}

 

思路是:用户第一次请求过来后,将url和用户id拼接作为key,1作为value值设置到redis中,并设置过期时间。用户下次请求过来,采用redis的自增,当value值超过最大访问次数时,拒绝用户访问。

以上是关于通过注解实现接口限流防刷的主要内容,如果未能解决你的问题,请参考以下文章

削峰限流防刷和令牌桶算法少卖问题解决方式

springboot Redis + HandlerInerceptor实现接口防刷

SpringBoot一个注解,实现接口防刷

SpringBoot一个注解,实现接口防刷

牛X!一个注解搞定接口防刷!

一个注解搞定 SpringBoot 接口防刷,还有谁不会?