面试官:接口被恶意狂刷,怎么办? 我:这个……没搞过(每天 CRUD,真没搞过) 面试官:那你现在设计一个? 我:巴拉巴拉……(自己都不信的胡扯) 面试官:(明显不耐烦)那我们换个话题吧。
说实话,那一刻我就知道—— 这场面试,已经凉了一半。
不是我不会写代码, 而是我从没系统想过「接口防刷」这种问题。
但后来我才发现一件事 接口防刷,其实一点都不复杂,甚至可以「一个注解搞定」。
今天这篇,我不跟你扯高深架构, 就用一个能在面试里讲清楚、在项目里用得上的防刷方案, 让你下次被问到,至少能稳稳说出 123。
-****01-
**接口防刷,本质在防什么? **
很多人一听“防刷”,脑子里就冒出:
风控
限流算法
网关
黑名单
滑块验证
其实对 80% 的业务系统来说,根本用不上这么重。
我们今天解决的是最常见的一类问题:
同一个用户 / 同一个接口,在短时间内被疯狂请求
所以防刷的核心只有一句话:
限制「同一个人」在「同一个接口」上的访问次数
[图片上传失败...(image-83c807-1768211127894)]
-****02-
**一个注解搞定防刷 **
先别急着看代码,先把思路在脑子里跑通
整体设计思路(面试版)
用自定义注解标记哪些接口需要防刷
用拦截器统一拦截请求
用 Redis记录访问次数
超过阈值,直接拒绝
一句话就是:
注解定义规则,拦截器执行规则,Redis 负责记账
第一步:自定义一个防刷注解
我们先定义一个注解,用来描述「防刷规则」。
<pre data-start="974" data-end="1147" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
@Retention(RUNTIME) @Target(METHOD) public @interface AccessLimit { // 允许访问的最大次数 intmaxCount(); // 是否需要登录 boolean needLogin()default false; }
</pre>
这个注解只干一件事:
把“防刷规则”写在方法上,而不是写死在代码里
用起来会非常优雅👇
<pre data-start="1203" data-end="1350" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
@AccessLimit(maxCount = 5, needLogin = true) @RequestMapping("/fangshua") @ResponseBody public Object fangshua() { return"请求成功"; }
</pre>
第二步:Redis 负责「记账」
防刷一定绕不开 Redis。
我们需要它做的事情很简单:
key:请求标识
value:访问次数
TTL:有效期
Redis Key 设计(重点,面试加分)
<pre data-start="1485" data-end="1520" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
URI + userId + yyyyMMdd
</pre>
也就是说:
同一个用户,在同一天,对同一个接口的访问次数
第三步:拦截器里干“脏活累活”
所有真正的防刷逻辑,都在拦截器里。
核心逻辑拆解(一定要能讲)
判断当前请求是否是 Controller 方法
判断方法上有没有
@AccessLimit组装 Redis key
从 Redis 取访问次数
超限 → 直接拦截
核心代码(精简但完整)
<pre data-start="1750" data-end="3151" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
@Component
</pre>
注册拦截器(别忘了这一步)
<pre data-start="3178" data-end="3455" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
@Configuration public class WebConfigextends WebMvcConfigurerAdapter { @Autowired private FangshuaInterceptor interceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(interceptor); } }
</pre>
到这一步,防刷链路已经完整闭环了。
效果验证
访问接口:
<pre data-start="3503" data-end="3541" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
http://localhost:8080/fangshua
</pre>
前 5 次:
请求成功第 6 次:
访问次数已达上限
一个注解,防刷生效。
[图片上传失败...(image-bf626e-1768211127894)]
-****03-
如果面试官继续追问,你可以这么答
Q1:这种方案适合什么场景?
适合中小系统、管理后台、业务接口级防刷 不适合超高并发网关级限流
Q2:有什么优化空间?
key 增加 IP 维度
使用
Redis Lua保证原子性与网关限流形成双层防护
Q3:为什么不用 AOP?
拦截器更贴近请求链路, AOP 更适合业务横切,不适合请求级控制
[图片上传失败...(image-916691-1768211127893)]
-****04-
总结
****接口防刷不是“高深架构”,而是“基础设计能力”****
你不需要一上来就说:
Sentinel
Gateway
滑块验证码
你只要能把:
****注解****
****拦截器****
****Redis****
****Key 设计****
**这条链路讲清楚, **面试官就已经在心里给你加分了。****