我们需要根据IP去限制用户单位时间的访问次数,防止刷手机验证码,屏蔽注册机等,使用注解就非常灵活了
1 定义注解
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- @Documented
- //最高优先级
- @Order(Ordered.HIGHEST_PRECEDENCE)
- public @interface RequestLimit {
- /**
- *
- * 允许访问的次数,默认值MAX_VALUE
- */
- int count() default Integer.MAX_VALUE;
- /**
- *
- * 时间段,单位为毫秒,默认值一分钟
- */
- long time() default 60000;
- }
2 实现注解
- @Aspect
- @Component
- public class RequestLimitContract {
- private static final Logger logger = LoggerFactory.getLogger("RequestLimitLogger");
- @Autowired
- private RedisTemplate<String, String> redisTemplate;
- @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")
- public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {
- try {
- Object[] args = joinPoint.getArgs();
- HttpServletRequest request = null;
- for (int i = 0; i < args.length; i++) {
- if (args[i] instanceof HttpServletRequest) {
- request = (HttpServletRequest) args[i];
- break;
- }
- }
- if (request == null) {
- throw new RequestLimitException("方法中缺失HttpServletRequest参数");
- }
- String ip = HttpRequestUtil.getIpAddr(request);
- String url = request.getRequestURL().toString();
- String key = "req_limit_".concat(url).concat(ip);
- long count = redisTemplate.opsForValue().increment(key, 1);
- if (count == 1) {
- redisTemplate.expire(key, limit.time(), TimeUnit.MILLISECONDS);
- }
- if (count > limit.count()) {
- logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
- throw new RequestLimitException();
- }
- } catch (RequestLimitException e) {
- throw e;
- } catch (Exception e) {
- logger.error("发生异常: ", e);
- }
- }
- }
3 自定义Exception
- public class RequestLimitException extends Exception {
- private static final long serialVersionUID = 1364225358754654702L;
- public RequestLimitException() {
- super("HTTP请求超出设定的限制");
- }
- public RequestLimitException(String message) {
- super(message);
- }
- }
4 在Controller中使用
- @RequestLimit(count=100,time=60000)
- @RequestMapping("/test")
- public String test(HttpServletRequest request, ModelMap modelMap) {
- //TODO
- }
我使用了redis缓存访问次数,并且设置自增1,其实用静态map也可以。