赞
踩
Spring Boot 防止接口刷新,通常是指防止接口被频繁地进行相同或类似的请求。
可以通过多种方式来防止接口被恶意刷取,以下是一些常见的方法:
1. 使用拦截器(Interceptor):
- 创建一个拦截器类,实现`HandlerInterceptor`接口。
- 在拦截器中重写`preHandle`方法,实现访问频率限制的逻辑。
- 将拦截器注册到Spring MVC的拦截器链中,这样每次请求都会经过拦截器的处理。
2. 使用过滤器(Filter):
- 创建一个过滤器类,实现`Filter`接口。
- 在过滤器中实现对请求的处理逻辑,比如检查请求的频率。
- 将过滤器注册到Spring Boot的过滤器链中,确保所有请求都会通过这个过滤器。
3. 使用切面(Aspect):
- 创建一个切面类,并使用`@Aspect`注解声明它是一个切面。
- 在切面中定义切点(Pointcut)和通知(Advice),用于拦截特定的方法调用或对象访问。
- 在通知中实现防刷逻辑,比如记录访问次数和时间,判断是否超过阈值。
4. 使用限流组件:
- 利用第三方库如Bucket4j、Sentinel等来实现限流。
- 这些库通常提供了丰富的配置选项和策略,可以方便地集成到Spring Boot项目中。
5. 使用API网关:
- 如果使用微服务架构,可以在API网关层面实现限流。
- API网关如Zuul、Kong等通常提供限流功能,可以在网关级别控制流量。
6. 使用Nginx配置:
- 在Nginx配置文件中设置限流规则,控制客户端的连接数和请求速率。
以下是个简单的例子,采用自定义注解的方式,根据时间来判断访问的次数做为限制
自定义注解
- import org.springframework.core.annotation.*;
- import org.springframework.core.Ordered;
- import java.lang.annotation.*;
-
-
- /**
- * @Author:myth
- * @time:
- * @Discription:
- */
- @Documented
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @Order(Ordered.HIGHEST_PRECEDENCE)
- public @interface RequestLimit {
- int count() default Integer.MAX_VALUE;
- long time() default 60000;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
主要实现类
- package com.example.common;
-
- import com.example.exception.CustomException;
- 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;
-
- /**
- * @method: $
- * @description: 限制访问类
- * @date: $
- * @author: myth
- * @return $
- */
- @Aspect
- @Component
- public class RequestLimitContract{
- private static final Logger logger = LoggerFactory.getLogger("requestLimitLogger");
- private Map<String , Integer> redisTemplate = new HashMap<>();
-
- @Before("within(@org.springframework.web.bind.annotation.RestController *) && @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 CustomException(ResultCode.LIMIT_NUMBER);
- }
- }catch (Exception e){
- logger.error("发生异常",e);
- throw new CustomException(ResultCode.LIMIT_NUMBER);
- }
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
自定义异常
- package com.example.common;
-
- /**
- * @method: $
- * @description: 描述一下方法的作用
- * @date: $
- * @author: myth
- * @return $
- */
- public class RequestLimitException extends Exception{
- private static final long serialVersionUID = 1364225358754654702L;
-
- public RequestLimitException(){
- super("1分钟内最多只能访问5次!");
- }
-
- public RequestLimitException(String message){
- super(message);
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
控制类中使用
- @PostMapping("/login")
- //1分钟内只允许访问5次,超过之后将会被禁止1分钟
- @RequestLimit(count=5,time=60000)
- public Result<Account> login(@RequestBody Account account, HttpServletRequest request) {
- if (StrUtil.isBlank(account.getName()) || StrUtil.isBlank(account.getPassword()) || account.getLevel() == null) {
- throw new CustomException(ResultCode.PARAM_LOST_ERROR);
- }
- }
总的来说,在选择具体的防刷手段时,需要根据系统的具体需求和场景来决定。例如,如果对性能要求较高,可以考虑使用基于内存的限流算法;如果需要更复杂的控制策略,可以使用专门的限流组件或API网关。同时,还需要考虑到系统的扩展性和维护性,选择最适合当前和未来需求的方案。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。