请求次数拦截器 | 青训营笔记

121 阅读1分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天

上一节写了如何用一个注解动态管理用户能看到的数据。本节学习如何用注解请求用户请求某个方法在一段时间内的次数限制。

在日常开发当中,我们经常有需要限制用户请求次数的需要。比如用户获取手机验证码,每分钟只能申请一次,要是不做限制,短时间内发送了很多条,这每一条都是成本啊,很不安全。还有就是一些比较复杂逻辑的接口,每次执行的时间比较长,如果一次性来太多请求的话就会极大的增加服务器压力,因此短时间内也不能让用户请求多次,所以也需要限制用户请求。本节学习如何用注解加拦截器的方式限制用户的请求次数。

注解定义

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestLimit {

    int seconds() default 60;//时间范围 单位:秒
    int maxCount() default 1;//最大次数
    String code();//方法标记,用于隔离
}

拦截器

@Slf4j
@Component
public class RequestLimitInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断请求是否属于方法的请求
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;

            //获取方法中的注解,看是否有该注解
            RequestLimit accessLimit = handlerMethod.getMethodAnnotation(RequestLimit.class);
            if (accessLimit == null) {
                return true;
            }

            int seconds = accessLimit.seconds();//单位时间
            int maxCount = accessLimit.maxCount();//最大次数
            String code = accessLimit.code();
            String ip = request.getLocalAddr();//ip地址

            //从redis中获取用户访问的次数
            String key = RedisConstants.REQUEST_LIMIT  + code + ip;
            Integer count = redisService.getObject(key, Integer.class);

            if (count == null) {
                //第一次访问
                redisService.set(key,1,seconds,TimeUnit.SECONDS);
            } else if(count < maxCount){
                Long expire = redisService.getExpire(key);
                redisService.set(key,count + 1,expire,TimeUnit.SECONDS);
            } else {
                throw new GlobalException(new CodeMsg("请求太频繁了,请稍后再试"));
            }
        }
        return true;
    }

}

使用范例

@GetMapping("/user/vCode")
@ApiOperation(value = "获取图片验证码", notes = "@author 摆渡人")
@NoNeedLogin
@RequestLimit(code = "user:phoneCode:",seconds = 10)
public Result<KaptchaVO> verificationCode() {
    return userService.sendImgCode();
}