https://blog.csdn.net/qq_38095094/article/details/78709433 有時候存在着一些惡意訪問的狀況,爲了阻止這種狀況的發生,咱們能夠寫一個攔截器,當某個IP的訪問在單位時間內超過必定的次數時,將禁止他繼續訪問。 在這裏咱們使用了SpringBoot搭配註解來使用 除了springboot須要的依賴以外,咱們還須要加上Aspect依賴java
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->web
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.10.RELEASE</version> </dependency>
咱們能夠自定義一個註解,裏面填入單位時間長度和單位時間訪問的最大次數redis
package com.example.web.demo.test;
import org.springframework.core.Ordered;spring
import org.springframework.core.annotation.Order;springboot
import java.lang.annotation.*;app
/**ide
@Author:高鍵城url
@time:.net
@Discription: / @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented @Order(Ordered.HIGHEST_PRECEDENCE) public @interface RequestLimit { /*code
/**
接下來咱們要定義一個異常類
package com.example.web.demo.test;
/**
public class RequestLimitException extends Exception {
private static final long serialVersionUID = 1364225358754654702L; public RequestLimitException(){ super("HTTP請求超出設定的限制"); } public RequestLimitException(String message){ super(message); }
}
有了註解以後,咱們要對註解實行一些相應的操做
package com.example.web.demo.test;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask;
/**
@Aspect
@Component
public class RequestLimitContract {
private static final Logger logger = LoggerFactory.getLogger("requestLimitLogger"); private Map<String , Integer> redisTemplate = new HashMap<>(); @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)") public void requestLimit(final JoinPoint joinPoint , RequestLimit limit) throws RequestLimitException { 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(url).concat(ip); if (redisTemplate.get(key) == null || redisTemplate.get(key) == 0) { redisTemplate.put(key, 1); } else { redisTemplate.put(key, redisTemplate.get(key) + 1); } int count = redisTemplate.get(key); if (count > 0) { //建立一個定時器 Timer timer = new Timer(); TimerTask timerTask = new TimerTask() { @Override public void run() { redisTemplate.remove(key); } }; //這個定時器設定在time規定的時間以後會執行上面的remove方法,也就是說在這個時間後它能夠從新訪問 timer.schedule(timerTask, limit.time()); } if (count > limit.count()) { logger.info("用戶IP[" + ip + "]訪問地址[" + url + "]超過了限定的次數[" + limit.count() + "]"); throw new RequestLimitException(); } }catch (RequestLimitException e){ throw e; }catch (Exception e){ logger.error("發生異常",e); } }
}
@before 註解表明在請求發送到控制器以前會先來到這裏執行相應的內容,within裏面的書寫表示寫在控制器上方而且有對應註解的控制器會來到這裏。 在得到ip和對應的url以後將它做爲一個key,存放到map中,假如map中已經存在了這個key,那麼多產生一個時間處理器,當時間超過註解書寫的單位時間以後又會將這個key從map中移走。 假如訪問的次數超過了限制,將會拋出異常說明訪問次數過多 最後在使用控制器的時候加上對應的註解便可
@Controller
public class testController{
@RequestMapping("/hello") @RequestLimit(count=10,time=60000) public String test(HttpServletRequest request){ return "hello"; }
}
小結:值得注意的是實現RequestLimit註解的方法必須有HttpServletRequest參數