防止利用恶性URL访问刷爆服务器攻击,利用自定义注解实现这类攻击的防御操作。
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. 定义一个注解的具体实现类(aop切面类),利用redis缓存访问url,ip以及访问次数
@Aspect
@Component
public class LimitSubmitAspect {
private static final Logger logger = LoggerFactory.getLogger("RequestLimitLogger");
@Autowired
private RedisService redisService;
@Before("@annotation(limit)")
public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RuntimeException {
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 = request.getLocalAddr();
String url = request.getRequestURL().toString();
String key = "req_limit_".concat(ip).concat(url);
String redisUrl;
try {
redisUrl = redisService.get(key).toString();
} catch (Exception e) {
redisUrl = null;
}
int index = 1;
if (redisUrl == null) {
redisService.set(key, index);
} else {
int num = (int) redisService.get(key);
redisService.set(key, num + 1);
}
int count = (int) redisService.get(key);
if (count > 0) {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
redisService.delete(key);
}
};
timer.schedule(task, limit.time());
}
if (count > limit.count()) {
logger.info("HTTP请求超出设定的限制");
throw new RuntimeException();
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
logger.error("发生异常: ", e);
}
}
}
3. 实现一个控制类,并添加使用注解。
/**
* 限制用户访问接口数量 防止恶意刷新接口
* @param request
* @return
*/
@RequestLimit(count = 2, time = 10000)
@GetMapping("/index")
public String index(HttpServletRequest request) {
System.out.println("方法执行");
return "hello";
}
这种方法只能在控制器这个层次上面的url拦截。