SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求

自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {

    /**
     * 允许访问的次数,默认值20
     */
    int count() default 20;

    /**
     * 时间段,单位为毫秒,默认值一分钟
     */
    long time() default 60000;
}

Aop增强类:

@Component
@Aspect
@Slf4j
public class RequestLimitAop {

    private Logger LOGGER = LoggerFactory.getLogger(getClass());

    @Autowired
    private RedisService redisService;

    @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")
    public void requestLimit(JoinPoint joinPoint, RequestLimit limit) throws Exception {
        Object[] args = joinPoint.getArgs();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        String ip = request.getRemoteAddr();
        LOGGER.info("访问的ip地址为:{}", ip);
        String url = request.getRequestURL().toString();
        String key = "req_limit_".concat(url).concat("_").concat(ip);
        boolean checkResult = checkByRedis(limit, key);
        if (!checkResult) {
            LOGGER.info("requestLimited," + "[用户ip:{}],[访问地址:{}]超过了限定的次数[{}]次", ip, url, limit.count());
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Type", "text/json;charset=UTF-8");
            response.setHeader("icop-content-type", "exception");
            PrintWriter writer = null;
            JsonGenerator jsonGenerator = null;
            try {
                writer = response.getWriter();
                jsonGenerator = (new ObjectMapper()).getFactory().createGenerator(writer);
                jsonGenerator.writeObject("请求过于频繁,超出限制!");
            } catch (IOException e1) {
                e1.printStackTrace();
            } finally {
                writer.flush();
                writer.close();
            }
            throw new Exception("请求过于频繁,超出限制!");
        }
    }

    private boolean checkByRedis(RequestLimit limit, String key) {
        Integer incrByCount = redisService.incrBy(key, limit);
        if (incrByCount > limit.count()) {
            /**
             * 该次请求已经超过了规定时间范围内请求的最大次数
             */
            LOGGER.info("当前请求次数为:{},该次请求已经超过了规定时间范围内请求的最大次数", incrByCount);
            return false;
        } else {
            /**
             * 该次请求已经未超过了规定时间范围内请求的最大次数,可以继续请求
             */
            LOGGER.info("当前请求次数为:{},该次请求已经未超过了规定时间范围内请求的最大次数,可以继续请求", incrByCount);
            return true;
        }
    }

使用注解:

/**
     * 测试使用redis+aop实现限流,防止同一个IP在短时间内多次恶意访问系统接口
     *
     * @return
     */
    @RequestLimit(count = 10, time = 60000)
    @RequestMapping("/secRequestLimit")
    @ResponseBody
    public String secRequestLimit(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {
        return username + "访问成功";
    }

 

使用jemeter测试:

SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求_第1张图片

SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求_第2张图片

 

SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求_第3张图片

打印的log日志:

SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求_第4张图片

 

SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求_第5张图片

SpringBoot+Aop+Redis+自定义注解来实现防止同一个ip在短时间内恶意多次请求_第6张图片

 

你可能感兴趣的:(redis,springboot,idea)