必会接口防刷案例

后端接口防刷实现:拦截器+Redis策略
文章介绍了如何使用拦截器结合Redis来防止恶意访问和重复提交,通过在拦截器中对特定接口进行注解,记录请求URI和用户信息的唯一Key及访问次数,利用Redis的过期机制限制接口在一定时间内的访问频率。当请求超出预设阈值时,系统将阻止进一步的访问并返回错误信息。
部署运行你感兴趣的模型镜像

拦截器+Redis

为了防止恶意访问接口造成服务器和数据库压力增大导致瘫痪,接口防刷(防止重复提交)在工作中是必不可少的,web项目前端也能够实现,我们要介绍的是后端如何实现接口防刷。

实现思路

由于本人能力有限,只接触过集群部署,一般都是使用两种方案解决,一种是拦截器+Redis实现,另外一种是使用拦截器+Guava Cache等本地缓存实现,此处介绍第一种。

实现原理是利用拦截器拦截所有接口请求,然后对需要防刷的接口使用注解标识,在拦截器中判断使用注解的方法,将根据请求的URI和用户信息生成唯一的Key和访问次数存放到redis中,之后的每次请求都会使访问次数加一。

利用redis能够过期的特性设定好一个访问周期的间隔时间。

实现目标:两次请求时间间隔5秒不算重复提交,但30秒内调用5次以上判定为恶意访问。

接下来我们来实现吧

具体实现

自定义一个注解AccessLimit,seconds为设置的秒数范围,maxCount是范围时间内可以访问的次数,needLogin与本文无关可忽略。

@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
    int seconds();
    int maxCount();
    boolean needLogin() default true;
}
复制代码

创建一个拦截器,继承HandlerInterceptorAdapter,在preHandle方法中做具体的操作。

每次请求都会根据key查询redis获取其访问次数,如果没有则是第一次访问,往redis中插入数据,过期时间是注解中的属性值seconds。

@Component
public class RepeatRequestInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisUtils redisUtils;

    @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中获取用户访问的次数(redis中保存的key保存30秒,redisUtils使用的单位是秒)
            Integer count = redisUtils.get(key,Integer.class,seconds);
            if(count == null){
                //第一次访问 key保存5秒 5秒后再访问key已过期,会重新生成
                redisUtils.set(key,1,5);
            }else if(count < maxCount){
                //加1
                redisUtils.increment(key);
            }else{
                //超出访问次数
                render(response);
                return false;
            }
        }
        return true;
    }
    private void render(HttpServletResponse response)throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = "{'mdg':'请求次数太多了'}";
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}
复制代码

附上redisUtils代码

@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ValueOperations<String, String> valueOperations;
    /**
     * 不设置过期时长
     */
    public final static long NOT_EXPIRE = -1;

    /**
     * 设置key value
     */
    public void set(String key, Object value){
        set(key, value, CacheConstant.DEFAULT_EXPIRE);
    }

    public void set(String key, Object value, long expire){
        valueOperations.set(key, toJson(value));
        if(expire != NOT_EXPIRE){
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    /**
     * 根据key获得对象
     */
    public <T> T get(String key, Class<T> clazz) {
        return get(key, clazz, NOT_EXPIRE);
    }

    public <T> T get(String key, Class<T> clazz, long expire) {
        String value = valueOperations.get(key);
        if(expire != NOT_EXPIRE){
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value == null ? null : fromJson(value, clazz);
    }

    /**
     * 根据key获得value
     */
    public String get(String key) {
        return get(key, NOT_EXPIRE);
    }
    public String get(String key, long expire) {
        String value = valueOperations.get(key);
        if(expire != NOT_EXPIRE){
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value;
    }

    public void increment(String key) {
        redisTemplate.opsForValue().increment(key,1L);
    }
}
复制代码

通过javaconfig形式把Interceptor注册到容器中

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private RepeatRequestInterceptor interceptor;

   
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "HEAD", "POST","PUT", "DELETE", "OPTIONS")
                .allowCredentials(true).maxAge(3600);
    }
}
复制代码

接口调用

编写一个测试类,写一个测试接口,如下

@RestController
@RequestMapping("/bid-applicant")
public class BidApplicantController extends BaseController {

    @AccessLimit(seconds=30, maxCount=5, needLogin=true)
    @RequestMapping("/fangshua")
    public ResponseInfo fangshua(){
        return ResponseInfo.ok("请求成功");
    }
复制代码

测试

我在第一次请求后的30秒内连续访问超过5次请求,会输出我的报错信息,工作中可以跳转自己的错误页面。

感谢阅读。

作者:Javaer2Leader
原文链接:https://juejin.cn/post/7109096387886710815
 

您可能感兴趣的与本文相关的镜像

HunyuanVideo-Foley

HunyuanVideo-Foley

语音合成

HunyuanVideo-Foley是由腾讯混元2025年8月28日宣布开源端到端视频音效生成模型,用户只需输入视频和文字,就能为视频匹配电影级音效

<think>嗯,用户之前详细询问了智能路由器的功能特点,现在突然转向路由器系统的操作细节,这个转向很有意思。可能是在了解智能路由器优势后,想通过机进一步释放硬件潜力?毕竟引用[2]提到机能让普通路由器媲美企业级设备,这对技术爱好者很有吸引力。 用户提供的四个引用很有价值:引用[1]提到机工具简化流程,引用[2]强调硬件改造可行性,引用[3][4]则具体涉及OpenWrt机操作和救砖警告。需要特别注意用户隐去的风险诉求——引用[3]的“变砖”警告和引用[4]的固件选择说明暗示用户更关注安全性而非纯技术流程。 准备分三个层面回应:方法上强调关键步骤(固件选择/不死引导/安全入),注意事项突出变砖预和数据备份,最后补充机价值。对于引用[1]的breed工具,要重点说明其“双保险”机制如何降低风险;硬件改造部分则引用[2]的RAM/Flash升级案例,但需提醒普通用户谨慎操作。 突然想到用户可能是极客群体——他们往往更关注引用[3]的OpenFlow协议这类高阶功能,所以最后补充应用场景时特意列入网络实验和边缘计算。生成的问题也侧重实战:变砖处理放在第一顺位,性能对比则呼应之前智能路由器的AI优化讨论。 对了,所有命令和路径名称都用代码块标注,像引用[4]的192.168.1.1这种IP必须明确格式。数学公式倒是不需要,但机耗时估算这类数据得用$T \propto \frac{F}{B}$ 的格式呈现才专业。 </think>以下是路由器系统的详细方法和注意事项,结合引用内容及技术实践整理: --- ### **一、机方法** #### **1. 准备工作** - **固件选择** 根据路由器型号下载匹配的第三方固件(如OpenWrt、DD-WRT),优先选用`factory.bin`(首次机)或`sysupgrade.bin`(升级现有第三方系统)[^3][^4]。 - **工具准备** 使用机工具(如`Breed Web控制台助手v5.9`)简化流程[^1],或通过路由器后台(192.168.1.1)的固件更新页面操作[^4]。 - **备份原固件** 在管理界面导出原厂固件和配置文件,防止机失败后无法恢复。 #### **2. 机流程** ```markdown 1. **连接路由器** - 电脑通过网线直连路由器LAN口,关闭火墙和杀毒软件。 2. **进入机模式** - 断电后长按Reset键通电,待指示灯闪烁后进入恢复模式(部分型号需用TTL线)[^3]。 3. **上传固件** - 浏览器访问 `192.168.1.1` 进入Breed Web或原厂恢复界面; - 选择下载的固件文件(如`openwrt-factory.bin`)并上传[^3][^4]。 4. **等待写完成** - 进度条走完前 **严禁断电**,成功后路由器自动重启(耗时约3-5分钟)。 ``` --- ### **二、关键注意事项** #### **1. 风险规避** - **变砖预** - 机中途断电必变砖!需准备TTL串口线或编程器救砖[^3]。 - 确保固件与硬件型号**完全匹配**(如MT7621芯片需选对应版本)。 - **分区保留** - OpenWrt时保留`Bootloader`分区,避免覆盖不死引导(如Breed)[^1]。 #### **2. 硬件限制** - **硬件改造需求** - 部分老旧路由器需升级Flash(存储芯片)或RAM才能运行大型固件(如OpenWrt)[^2]。 - 加装USB接口需焊接和驱动支持,非专业用户慎操作[^2]。 #### **3. 软件兼容性** - **固件校验** - 验证固件MD5值,防止文件损坏导致机失败。 - **配置重置** - 机后首次启动需 **恢复出厂设置**,避免新旧配置冲突。 --- ### **三、机后的进阶操作** 1. **安装插件** OpenWrt通过`opkg`命令安装插件(如广告过滤`adblock`、NAS服务): ```bash opkg update && opkg install luci-app-adblock ``` 2. **网络协议配置** 支持入OpenFlow 1.3协议实现SDN功能(需Linux系统基础)[^3]。 3. **性能调优** 超频CPU、启用硬件NAT加速提升吞吐量($$ \text{实际带宽} = \frac{\text{数据包量}}{\text{处理延迟}} $$)。 --- ### **四、推荐工具与资源** | 工具类型 | 推荐选项 | 用途 | |----------------|-------------------------|--------------------------| | **不死引导** | Breed Web控制台 | 变砖/多固件支持 [^1] | | **固件源** | OpenWrt官网 | 稳定版固件下载 | | **救砖工具** | CH341A编程器+夹子 | 修复Flash芯片变砖 | > ⚠️ **警示**:机可能失去官方保修,且操作失误会导致硬件损坏!新手建议从支持免拆机的型号(如小米AC2100)入手。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值