接口访问限制实现
目的
限制用户在一定时间内只能访问接口的次数。
后端限制方法
原理
1.通过 Redis 记录请求次数,如果超过限制则拒绝访问。同时设置 Redis 的 key 的时效性,过期自动销毁。
2.使用限流器
自定义注解(Java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface AccessLimit { int seconds();
int maxCount(); }
|
配置拦截器(Java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Component public class LimitInterceptor implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(LimitInterceptor.class);
@Autowired private RedisCache redisCache;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if (accessLimit == null) { return true; }
int seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); String key = request.getServletPath() + request.getSession().getId();
Integer count = 0; if (redisCache.getCacheObject(key) != null) { count = (Integer) redisCache.getCacheObject(key); }
if (count == 0) { redisCache.setCacheObject(key, 1); redisCache.expire(key, seconds, TimeUnit.SECONDS); logger.info("{} 第一次访问", key); } else if (count < maxCount) { redisCache.incr(key, 1); } else { logger.error("超出访问次数"); render(response, "请求次数过于频繁!"); return false; } } return true; }
private void render(HttpServletResponse response, String msg) throws Exception { response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); out.print(new ObjectMapper().writeValueAsString(RespBean.error(msg))); out.flush(); out.close(); } }
|
注册拦截器(Java)
1 2 3 4 5 6 7 8 9 10 11 12
| @Configuration public class WebConfig implements WebMvcConfigurer { @Bean public LimitInterceptor limitHandlerInterceptor() { return new LimitInterceptor(); }
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(limitHandlerInterceptor()); } }
|
在 Controller 的接口加上注解(Java)
1 2 3 4 5 6 7 8
| @PostMapping("/") @AccessLimit(seconds = 6, maxCount = 1) public RespBean addEmp(@RequestBody Employee employee) { if (employeeService.addEmp(employee)) { return RespBean.ok("添加成功!"); } return RespBean.error("添加失败!"); }
|