需求
💡 Tips:注解使用起来简单快速,配置传入2个参数即可,每个方法可设置不同值。
● 防止同一个ip地址,短时间内多次请求接口。
● 用到自定义注解、HandlerInterceptor
、Redis
、IpUtils
,实现接口限流。
创建自定义注解
💡 Tips:全忘完了。
● @interface
先声明一个自定义注解AccessLimit。
● 2个属性:senconds
秒、maxCount
最大请求次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AccessLimit {
int seconds();
int maxCount(); }
|
实现HandlerInterceptor
接口,拦截请求
● 编写WebSecurityHandler
方法,实现HandlerInterceptor
接口。
● 重写preHandle
方法,在方法调用前进行拦截。
● 从方法中获取我们自定义的@AccessLimit注解,如果获取到了,则进行逻辑处理。
● 从注解中获取配置的值:秒数 以及最大次数;从httpServletRequest
中获取ip地址和方法名。
● 将ip地址+方法名
做为key
值,值为1
(自增1),过期时间为seconds
存入redis
中。
● 将自增后的结果返回,与注解中的maxCount比较大小,如果大则拦截,反之则放行。
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
| @Log4j2 public class WebSecurityHandler implements HandlerInterceptor { @Autowired private RedisService redisService;
@Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if (accessLimit != null) { long seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); String key = IpUtils.getIpAddress(httpServletRequest) + hm.getMethod().getName(); try { long q = redisService.incrExpire(key, seconds); if (q > maxCount) { render(httpServletResponse, Result.fail("请求过于频繁,请稍候再试")); log.warn(key + "请求次数超过每" + seconds + "秒" + maxCount + "次"); return false; } return true; } catch (RedisConnectionFailureException e) { log.warn("redis错误: " + e.getMessage()); return false; } } } return true; }
private void render(HttpServletResponse response, Result<?> result) throws Exception { response.setContentType(APPLICATION_JSON); OutputStream out = response.getOutputStream(); String str = JSON.toJSONString(result); out.write(str.getBytes(StandardCharsets.UTF_8)); out.flush(); out.close(); } }
|
Redis
的工具类
每次请求使当前key
中的value
值自增1
,并将value
结果返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class RedisServiceImpl implements RedisService { @Resource private RedisTemplate<String, Object> redisTemplate; @Override public Long incrExpire(String key, long time) { Long count = redisTemplate.opsForValue().increment(key, 1); if (count != null && count == 1) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } return count; } }
|