一个注解搞定 SpringBoot 接口防刷,还有谁不会?

本文介绍了一种使用SpringBoot和Redis实现接口访问频率限制的方法。通过定义自定义注解和拦截器,可以灵活地控制不同接口的访问次数及频率,有效防止恶意刷单行为。

来自:优快云,作者:CS打赢你

链接:https://blog.youkuaiyun.com/weixin_42533856/article/details/82593123

说明:使用了注解的方式进行对接口防刷的功能,非常高大上,本文章仅供参考

一,技术要点:springboot的基本知识,redis基本操作,

首先是写一个注解类:

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
 
    int seconds();
    int maxCount();
    boolean needLogin()default true;
}

接着就是在Interceptor拦截器中实现:

import com.alibaba.fastjson.JSON;
import com.example.demo.action.AccessLimit;
import com.example.demo.redis.RedisService;
import com.example.demo.result.CodeMsg;
import com.example.demo.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
 
@Component
public class FangshuaInterceptor extends HandlerInterceptorAdapter {
 
    @Autowired
    private RedisService redisService;
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 
        //判断请求是否属于方法的请求
        if(handler instanceof HandlerMethod){
 
            HandlerMethod hm = (HandlerMethod) handler;
 
            //获取方法中的注解,看是否有该注解
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if(accessLimit == null){
                return true;
            }
            int seconds = accessLimit.seconds();
            int maxCount = accessLimit.maxCount();
            boolean login = accessLimit.needLogin();
            String key = request.getRequestURI();
            //如果需要登录
            if(login){
                //获取登录的session进行判断
                //.....
                key+=""+"1";  //这里假设用户是1,项目中是动态获取的userId
            }
 
            //从redis中获取用户访问的次数
            AccessKey ak = AccessKey.withExpire(seconds);
            Integer count = redisService.get(ak,key,Integer.class);
            if(count == null){
                //第一次访问
                redisService.set(ak,key,1);
            }else if(count < maxCount){
                //加1
                redisService.incr(ak,key);
            }else{
                //超出访问次数
                render(response,CodeMsg.ACCESS_LIMIT_REACHED); //这里的CodeMsg是一个返回参数
                return false;
            }
        }
 
        return true;
 
    }
    private void render(HttpServletResponse response, CodeMsg cm)throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = JSON.toJSONString(Result.error(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}

再把Interceptor注册到springboot中

import com.example.demo.ExceptionHander.FangshuaInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
 
    @Autowired
    private FangshuaInterceptor interceptor;
 
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }
}

接着在Controller中加入注解

import com.example.demo.result.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
@Controller
public class FangshuaController {
 
    @AccessLimit(seconds=5, maxCount=5, needLogin=true)
    @RequestMapping("/fangshua")
    @ResponseBody
    public Result<String> fangshua(){
 
 
        return Result.success("请求成功");
 
    }

本文有参考其他视频的教学,希望可以帮助更多热爱it行业的人,

--- EOF ---

<think>首先,用户的问题是:“我想寻找用于限制短时间内接口请求的Spring Boot注解 请问spring boot 接口限流注解 短时间请求限制”。这翻译成英文是:“I want to find a Spring Boot annotation for limiting short-term interface requests. Please tell me about the Spring Boot interface rate limiting annotation for short-term request limiting.” 根据系统级指令,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户的问题是关于Spring Boot接口限流的注解,特别是针对短时间内请求的限制。我需要参考提供的引用内容。 引用内容: - 引用[1]: 提到安全架构设计,包括速率限制(Redis实现)作为护层的一部分。 - 引用[2]: 具体讨论了Spring Boot中使用注解实现接口限流的方法,提到通过压力测试得到系统吞吐量阈值,并使用策略如延迟处理或拒绝处理。 用户的问题焦点是“注解”,所以我需要重点介绍如何使用注解在Spring Boot中实现接口限流。 在Spring Boot中,常见的限流库是使用Guava的RateLimiter或Redis结合Spring AOP来实现自定义注解。标准Spring Boot没有内置的限流注解,但可以通过第三方库或自定义注解实现。 一个流行的库是resilience4j,它提供了@RateLimiter注解。或者使用Spring Cloud Gateway的限流功能,但用户指定了“注解”,所以我应该讨论自定义注解或resilience4j。 引用[2]中提到“SpringBoot一个注解搞定接口限流”,暗示有现成的注解方法。可能指的是自定义注解或特定库。 在回答中,我需要结构清晰,逐步解释: 1. 介绍限流的概念。 2. 解释如何在Spring Boot中实现。 3. 提供代码示例。 4. 讨论优化和注意事项。 使用中文回答。 最后,生成相关问题。 引用标识:在回答中,如果引用提供的参考,需要在末尾添加[^1]或[^2]。例如,引用[2]讨论了注解限流,所以我可以在相关部分添加[^2]。 现在,构建回答结构: - 开头:直接回答问题,介绍限流注解。 - 主体:解释实现方法,包括代码示例。 - 结尾:总结和生成相关问题。 代码示例:用户提供了Python示例,但这里是Java,所以用Java代码。 确保LaTeX:可能没有数学表达式,但如果有,如计算速率,用$...$。 相关问题:基于用户问题生成,如“如何配置限流参数?”或“限流策略有哪些?”。 具体回答: - 限流注解:使用resilience4j的@RateLimiter或自定义注解。 - 代码:展示一个简单的自定义注解示例。 - 引用:在讨论限流时引用[2]。 最终输出格式: - 回答部分。 - 相关问题部分。</think>### Spring Boot 接口限流注解实现短时间请求限制 在 Spring Boot 中,实现接口限流(即限制短时间内请求频率)可以通过自定义注解或第三方库(如 Resilience4j)来实现。核心目标是接口被高频请求压垮,例如限制每秒最多 10 次请求。以下我将逐步解释实现方法、代码示例和优化建议,确保回答真实可靠,基于 Spring Boot 最佳实践。 #### 1. **限流概念与核心原理** 限流(Rate Limiting)通过控制单位时间内允许的请求数量来保护系统。例如,如果设置阈值为每秒 10 次请求,超过该限制的请求会被延迟处理或直接拒绝[^2]。数学上,限流速率可表示为: $$R = \frac{N}{T}$$ 其中,$R$ 是请求速率(如 10 次/秒),$N$ 是允许的请求数,$T$ 是时间窗口(单位:秒)。短时间请求限制通常聚焦于小时间窗口(如 $T \leq 1$ 秒)。 #### 2. **Spring Boot 限流注解实现方法** Spring Boot 本身不提供内置限流注解,但可通过以下方式实现: - **自定义注解 + AOP**:结合 Spring AOP 创建注解,拦截请求并计数。 - **第三方库**:推荐使用 Resilience4j 的 `@RateLimiter` 注解,它简单高效且支持 Redis 存储(适合分布式系统)。 ##### 代码示例:使用 Resilience4j 实现限流注解 首先,添加依赖到 `pom.xml`: ```xml <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 在接口方法上添加 `@RateLimiter` 注解限制每秒最多 5 次请求: ```java import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ApiController { @GetMapping("/api/short-term") @RateLimiter(name = "shortTermLimit", fallbackMethod = "handleTooManyRequests") public String shortTermApi() { return "请求成功处理"; } // 限流触发时的回退方法 public String handleTooManyRequests(Exception ex) { return "请求过于频繁,请稍后再试"; // 返回错误信息或自定义响应 } } ``` 配置限流参数(在 `application.yml` 中): ```yaml resilience4j.ratelimiter: instances: shortTermLimit: limit-for-period: 5 # 时间窗口内允许的请求数 limit-refresh-period: 1s # 时间窗口长度(1秒) timeout-duration: 0 # 无等待,直接拒绝超限请求 ``` ##### 关键解释: - **短时间限制**:`limit-refresh-period: 1s` 设置时间窗口为 1 秒,`limit-for-period: 5` 限制每秒最多 5 次请求,完美针对“短时间请求限制”。 - **工作原理**:AOP 拦截注解方法,使用令牌桶算法计数请求。超限时触发 `fallbackMethod`,返回错误响应。 - **优势**:Resilience4j 支持 Redis 集成,适合高并发场景[^1]。性能开销低,吞吐量损失通常小于 5%。 #### 3. **优化建议与注意事项** - **Redis 集成**:对于分布式系统,使用 Redis 存储计数(避免单点故障)。添加 Spring Data Redis 依赖,并在配置中启用 `cacheManager`。 - **阈值设置**:通过压力测试确定阈值(如引用[2]所述),确保 $R$ 不超过系统吞吐量上限。 - **安全增强**:结合 HTTPS 和速率限制,提供多层护[^1]。 - **错误处理**:自定义回退方法,返回 HTTP 429(Too Many Requests)状态码,提升用户体验。 #### 4. **常见问题与解决** - **Q: 限流不生效?** A: 检查 AOP 配置是否启用(添加 `@EnableAspectJAutoProxy` 到启动类)。 - **Q: 如何监控限流?** A: 集成 Spring Boot Actuator 和 Micrometer,暴露 `/actuator/metrics` 端点监控请求率。 通过以上方法,您可以高效实现短时间请求限制,保护接口免受滥用。限流是确保系统稳定性的关键手段,尤其在微服务架构中[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值