文章目录
前言
在日常开发当中,遇到其他恶意攻击,恶意调用我们公共开放的接口,例如: 恶意调用短信接口,数以万计的请求,打到我们的公共短信服务,占用系统资源,其他用户无法完成其他业务操作。最终导致系统崩溃。
前些天提供了阿里的Canal组件的使用,今天就配合Canal组件,完成自定义请求拦截
一、解决方案
1:拦截器的原理
1:preHandle:在执行Handler之前(执行业务逻辑之前),根据拦截器链顺序执行;
2:postHandle:在执行Handler成功(执行业务逻辑成功)之后,根据拦截器链倒序执行,如果前面的流程中抛出异常或者请求被拦截则不会执行!
3:afterCompletion:在请求处理完毕之后执行,无论是否有响应视图,无论有没有通过preHandle,无论有没有抛出异常。只会对此前放行成功(preHandle返回true)的拦截器进行倒序调用
本质也就是一种AOP思想的实现,源码分析(后续)
2:自定义拦截注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE ,ElementType.METHOD})
@Inherited // 增强继承性
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE) // 设置优先级
public @interface RequestIntercept {
/* 次数*/
int count() default Integer.MAX_VALUE;
/* 默认时间: 1分钟*/
long time() default 60000;
}
二、使用步骤
1:自定义拦截器
@Component
@Slf4j
public class RequestLimitInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<Object ,Object> redisTemplate;
@Autowired
private SystemFeignService systemFeignService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, java.lang.Object handler) throws Exception {
HandlerMethod method = (HandlerMethod) handler;
RequestIntercept methodAnnotation = method.getMethodAnnotation(RequestIntercept.class);
RequestIntercept classAnnotation = method.getBean().getClass().getAnnotation(RequestIntercept.class);
request.setCharacterEncoding("UTF-8");
String[] split = request.getRequestURI().split("/");
String phone = split[split.length - 1];
if (methodAnnotation != null){
log.info("方法注解限制频率");
String ip = RequestUtil.getIP(request);
String url = request.getRequestURL().toString();
String key = "intercept_".concat(url).concat(ip);
Long count = redisTemplate.opsForValue().increment(key, 1);
int maxSize = methodAnnotation.count();
long timeOut = methodAnnotation.time();
if (count == 1 ){
redisTemplate.expire(key, timeOut, TimeUnit.MILLISECONDS);
return true;
}
if (count > maxSize ) {
// OpenFeign拉黑
systemFeignService.sava(Blacklist.builder()
.ip(ip).name(phone).phone(phone).createdTime(new Date()).status(1).build());
log.info(" 当前访问次数: " + count + ",已经成功限制!");
return false;
}
} else if (classAnnotation != null){
log.info("类注解限制频率");
return false;
}else {
return true;
}
return true;
}
}
没来得急优化代码,将就看吧。大致的逻辑就是:将我们的自定义注解使用在Controller的相应方法上。当我们请求就会就进入拦截器,判断当前请求是否是小于规定次数,小于就放行,大于就拦截,并使用canal同步信息到Redis,进行拉黑用户禁止后续操作(建议拉黑IP地址)
2:配置拦截器
在MVC项目中需要这样配置
在SpringBoot项目中,交给容器使用。
@Configuration
public class ResourcesConfig implements WebMvcConfigurer{
@Autowired
private RequestLimitInterceptor repeatSubmitInterceptor;
/**
* 自定义拦截规则
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
}
}
3:测试效果
第一次访问效果:
第二次访问效果:
数据库信息:
canal同步信息:
RocketMQ的可视化工具,已经接受到消息对象。在项目中写一个消费监听器,自动同步Redis即可。当用户再次登录获取IP,或其他参数进行业务处理即可。
总结
以上就是今天要讲的内容,有任何问题欢迎留言