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測試:

 

打印的log日誌:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章