赞
踩
显示中存在恶意ip频繁请求情况,本文通过自定义注解+拦截器实现限制ip访问的频率
1. 添加pom依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
2. 添加自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
/**
* 允许访问的最大次数
*/
int count() default Integer.MAX_VALUE;
/**
* 时间段,单位为毫秒,默认值一分钟
*/
long time() default 60000;
}
3. 添加自定义异常
public class RequestLimitException extends NestedRuntimeException {
public RequestLimitException(){
super("HTTP请求超出设定的限制");
}
public RequestLimitException(String msg) {
super(msg);
}
}
4. 添加自定义拦截器
interceptor
@Slf4j @Aspect @Component public class RequestLimitInterceptor { @Before("within(@org.springframework.web.bind.annotation.RequestMapping * || @javax.ws.rs.Path *) && @annotation(limit)") public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException { try { // 获取 HttpServletRequest 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 = HttpUtil.getIpByRequest(request); String url = request.getRequestURL().toString(); String key = "req_limit_".concat(url).concat(ip); IMap<String, Object> cache = CacheData.getInstance().getRequestLimitCache(); String value = (String) cache.get(key); if (null == value) { value = "1_" + System.currentTimeMillis(); cache.put(key, value, limit.time(), TimeUnit.MILLISECONDS); } else { String[] s = value.split("_"); int count = Integer.parseInt(s[0]); if (count > limit.count()) { log.info("用户IP[{}], 访问地址[{}], 超过了限定的次数[{}]", ip, url, limit.count()); throw new RequestLimitException(); } value = (count + 1) + "_" + s[1]; long last = limit.time() - (System.currentTimeMillis() - Long.parseLong(s[1])); if (last > 0) { cache.put(key, value, last, TimeUnit.MILLISECONDS); } } } catch (RequestLimitException e) { throw e; } catch (Exception e) { log.error("发生异常", e); } } }
http utils
@Slf4j public class HttpUtil { public static String getIpByRequest(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理后会有多个ip值,第一个ip才是真实ip if (ip.contains(",")) { ip = ip.split(",")[0]; } } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
5. 使用
Jersey方式
@RequestLimit(time = 3000,count = 2)
@GET
@Path("/test")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response test(@Context HttpServletRequest request) {
return Response.status(200).entity(new Date()).build();
}
Spring MVC 方式
@RequestLimit(time = 3000,count = 2)
@GetMapping(value = "/test")
public RespEntity test(HttpServletRequest request) {
return RespEntity.success(new Date());
}
本文优化了拦截方式,可以同时拦截springMVC和Jersey。优化了ip次数检查,利用的hazelcast过期key的方式,当然redis也可以实现。
参考:
https://blog.csdn.net/It_BeeCoder/article/details/94303699
https://bbs.csdn.net/topics/392154383
https://blog.csdn.net/qq_37272886/article/details/88553962
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。