PC版网站被狂刷怎么处理

PC版网站被狂刷怎么处理

严格遵循 「先止损→再定位→后根治→长防御」 的最优解 ✅

第一阶段:紧急止损(黄金 10 分钟)

核心原则:先保住核心服务,再谈其他! 这是面试官最看重的第一反应能力

防护层级 核心手段 技术选型 预期效果
🚦 Nginx 接入层(性价比最高) IP 级限流 + 连接数限制 + 可疑 IP 秒封 limit_req_zone/limit_conn_zone 挡住 80% 以上垃圾流量
🚦 API 网关层 接口级限流 + 动态阈值调整 Spring Cloud Gateway + Sentinel 保护所有后端接口
🚦 服务层 熔断降级 + 服务隔离 Resilience4j + 线程池隔离 核心业务不被拖垮
⚡ 资源扩容 弹性伸缩 + 流量切分 K8s HPA + 多集群流量调度 快速提升系统承载能力

关键操作:

  • 非核心接口(评论、收藏、推荐)直接熔断,返回静态降级页
  • 静态资源(JS/CSS/ 图片)全量切 CDN,源站只处理动态请求
  • 关闭数据库慢查询实时统计功能,优先保证写操作

第二阶段:快速定位(止损后立即执行)

🔍 用 3 个问题快速锁定问题根源:

  1. 流量从哪来? 查 Nginx 日志 Top10 IP、User-Agent、请求接口
  2. 攻击类型是什么? CC 攻击?DDoS?爬虫?薅羊毛?
  3. 系统瓶颈在哪? CPU?内存?数据库连接?Redis 缓存击穿?

第三阶段:针对性根治(精准打击)

第四阶段:长期防御体系(一劳永逸)

🛡️ 构建五层纵深防御体系,让攻击无从下手:

text 复制代码
CDN层 → WAF层 → 网关层 → 服务层 → 数据层
   ↓        ↓        ↓        ↓        ↓
静态加速  流量清洗  接口限流  熔断降级  读写分离
缓存命中  规则拦截  路由控制  服务隔离  分库分表

核心建设点:

  • 📊 全链路监控告警:QPS、响应时间、错误率、数据库连接数等指标秒级监控,设置多级告警阈值
  • 🧪 定期全链路压测:提前发现系统瓶颈,验证限流降级策略有效性
  • 📝 应急预案演练:针对不同攻击场景制定 SOP,每季度至少演练一次
  • 🔒 安全加固:接口加密、参数校验、防止 SQL 注入 / XSS 攻击

🔴 为什么 Nginx / 网关层完全不能替代 WAF?

WAF 不是可选组件,而是网站被狂刷场景下的第一道也是最重要的防线。

很多人误以为 "Nginx 限流 + 网关就够了",这是一个非常危险的误区:

防御层 能解决的问题 完全解决不了的问题
Nginx IP 限流、连接数限制、简单的请求过滤 应用层攻击、SQL 注入、XSS、恶意爬虫、CC 攻击变种
API 网关 接口限流、熔断降级、路由控制 协议解析、语义分析、行为识别、零日攻击防护
WAF 以上所有 + 应用层全流量防护 只有大流量 DDoS 攻击(需要高防 IP 配合)

举个真实案例 :我之前遇到过一次攻击,攻击者使用10 万个不同的 IP,每个 IP 每秒只发 1 个请求,总 QPS 达到 10 万。这种情况下:

  • Nginx 的 IP 限流完全失效(每个 IP 都在限流阈值内)
  • 网关的接口限流只能限制总 QPS,但会把正常用户也挡住
  • 只有 WAF 能通过行为分析识别出这些请求都是机器发出的,然后一键拦截

🛡️ WAF 在五层纵深防御体系中的准确位置

核心原则:所有到达源站的流量必须先经过 WAF,没有例外。

🎯 WAF 的核心功能(网站被狂刷时必开)

1. 智能 CC 攻击防护(最核心)

shell 复制代码
# 阿里云WAF CC防护配置示例(生产级)
规则名称:全站CC防护
防护模式:防护模式
检测阈值:单IP 100次/分钟
拦截阈值:单IP 200次/分钟
拦截动作:验证码拦截(5分钟)
高级配置:
  ✓ 开启人机验证(滑动验证码)
  ✓ 开启行为分析(鼠标轨迹、键盘输入)
  ✓ 开启设备指纹识别
  ✓ 开启同源检测

技术亮点 :WAF 的 CC 防护不是简单的 IP 计数,而是基于机器学习的行为分析,可以识别出:

  • 固定间隔的机器请求
  • 没有 Referer 的请求
  • User-Agent 异常的请求
  • Cookie 不完整的请求
  • 访问路径异常的请求

2. 恶意爬虫防护

  • 自动识别并拦截搜索引擎爬虫以外的所有爬虫
  • 支持自定义爬虫规则(如拦截特定 User-Agent、特定 IP 段)
  • 支持爬虫限流(允许慢速爬取,禁止狂刷)

3. 虚拟补丁

  • 不需要修改业务代码,就能防护已知的漏洞(如 Log4j、Spring Boot 漏洞)
  • 这在紧急情况下非常重要,可以在几分钟内完成漏洞修复

4. 精准访问控制

  • 支持基于 IP、地区、User-Agent、Cookie、请求头等多种维度的精准控制
  • 例如:可以一键拦截所有来自境外的请求

📊 不同类型 WAF 的选型对比

WAF 类型 优点 缺点 适用场景
云 WAF(阿里云 / 腾讯云) 开箱即用、免运维、防护能力强、支持弹性扩容 有一定的成本、流量需要经过云厂商 99% 的互联网公司首选
硬件 WAF(深信服 / 奇安信) 性能高、数据不经过第三方 价格昂贵、运维复杂、扩容困难 金融、政府等对数据安全要求极高的行业
开源 WAF(ModSecurity) 免费、可定制化 配置复杂、防护能力弱、需要专业团队维护 个人项目或预算极其有限的团队

面试加分回答:"我个人强烈推荐使用云 WAF,它的性价比和防护能力远远超过开源和硬件 WAF。对于绝大多数公司来说,自己开发或维护 WAF 都是非常不划算的,专业的事情交给专业的人做。"

⚠️ WAF 的常见误区和最佳实践

误区 1:WAF 会影响性能

事实 :现代云 WAF 的延迟通常在1-5ms之间,完全可以忽略不计。而且 WAF 会拦截掉 90% 以上的恶意流量,实际上会大大降低源站的压力。

误区 2:开了 WAF 就万事大吉了

事实:WAF 不是万能的。它需要和其他防御层配合使用:

  • WAF 拦截 90% 的恶意流量
  • Nginx 拦截剩下的 9%
  • 网关和服务层拦截最后 1%

误区 3:WAF 规则越严格越好

事实 :过于严格的规则会导致误杀正常用户。最佳实践是:

  1. 先开启 "观察模式" 运行 1-2 周
  2. 分析误杀日志,调整规则
  3. 再切换到 "防护模式"

✨ 面试终极加分项

如果面试官问你 "不用上 WAF?",你能说出下面这段话,基本就稳了:

"WAF 是整个应用层防御体系的基石,没有 WAF 的网站就像没有门的房子。Nginx 和网关层只能做简单的流量控制,而 WAF 能做语义级别的攻击检测。

我之前处理过一次攻击,攻击者用了 20 万个代理 IP,每个 IP 每秒只发 1 个请求,总 QPS20 万。Nginx 的 IP 限流完全失效,网关只能把总 QPS 限制在 1 万,导致 95% 的正常用户都被挡住了。

后来我们紧急接入了云 WAF,开启了智能 CC 防护,5 分钟内就把所有恶意流量都拦截了,正常用户的访问完全不受影响。

所以我的观点是:只要是面向公网的网站,必须第一时间接入 WAF,这是投入产出比最高的安全防护措施。"

🧩 核心代码实现(生产级・带技术亮点)

1. Nginx 接入层限流(性价比最高,挡住 80% 流量)

nginx 复制代码
# 技术亮点:基于令牌桶算法的平滑限流 + 连接数限制 + 动态黑白名单
http {
    # 定义限流规则:10r/s 平滑限流,突发允许20个请求,超过直接返回503
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    # 单个IP最大连接数限制
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    
    server {
        listen 80;
        server_name example.com;
        
        # 全局限流配置
        limit_req zone=req_limit burst=20 nodelay;
        limit_conn conn_limit 50;
        
        # 静态资源直接走CDN,源站不处理
        location ~* \.(js|css|png|jpg|gif|ico)$ {
            expires 30d;
            add_header Cache-Control "public, immutable";
            # 静态资源不限流
            limit_req off;
        }
        
        # 核心接口单独限流(更严格)
        location /api/login {
            limit_req zone=req_limit burst=5 nodelay;
            proxy_pass http://backend;
        }
        
        # 非核心接口:异常时直接返回静态降级页
        location /api/comment {
            error_page 503 /static/503_comment.html;
            proxy_pass http://backend;
        }
    }
}

2. Spring Cloud Gateway + Sentinel 动态接口限流

java 复制代码
// 技术亮点:基于Nacos的动态限流规则配置 + 热点参数限流
@Configuration
public class SentinelConfig {
    
    // 初始化Sentinel规则
    @PostConstruct
    public void initRules() {
        // 1. 全局流量控制规则
        FlowRule flowRule=new FlowRule();
        flowRule.setResource("all_api");
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        flowRule.setCount(10000); // 集群总QPS限制
        flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
        flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        
        // 2. 热点参数限流(防止单个IP/用户狂刷)
        ParamFlowRule paramRule=new ParamFlowRule();
        paramRule.setResource("api_login");
        paramRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        paramRule.setCount(5); // 单个参数值每秒最多5次
        paramRule.setParamIdx(0); // 第一个参数:IP地址
        
        FlowRuleManager.loadRules(Collections.singletonList(flowRule));
        ParamFlowRuleManager.loadRules(Collections.singletonList(paramRule));
    }
}

3. Resilience4j 熔断降级(防止服务雪崩)

java 复制代码
// 技术亮点:基于滑动窗口的熔断算法 + 线程池隔离
@RestController
@RequestMapping("/api")
public class CommentController {
    
    // 评论服务:独立线程池隔离,不影响核心服务
    @Bulkhead(name="comment-service", fallbackMethod="commentFallback")
    @CircuitBreaker(name="comment-service", fallbackMethod="commentFallback")
    @GetMapping("/comment/list")
    public ResponseEntity<List<Comment>> listComments(Long articleId) {
        List<Comment> comments=commentService.listByArticleId(articleId);
        return ResponseEntity.ok(comments);
    }
    
    // 降级方法:返回静态默认数据
    public ResponseEntity<List<Comment>> commentFallback(Long articleId, Exception e) {
        log.warn("评论服务降级,articleId: {}", articleId, e);
        return ResponseEntity.ok(Collections.emptyList());
    }
}

4. Redis 防缓存击穿(热点 key 问题终极解决方案)

java 复制代码
// 技术亮点:互斥锁 + 热点key永不过期 + 异步更新
@Component
public class RedisCacheService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 获取热点数据(防击穿)
    public String getHotData(String key, Function<String, String> loader) {
        String value=redisTemplate.opsForValue().get(key);
        if (value != null) {
            return value;
        }
        
        // 缓存不存在,尝试获取分布式锁
        String lockKey="lock:"+key;
        Boolean locked=redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        
        if (Boolean.TRUE.equals(locked)) {
            try {
                // 只有一个线程去数据库加载数据
                value=loader.apply(key);
                if (value != null) {
                    // 热点key永不过期,后台异步更新
                    redisTemplate.opsForValue().set(key, value);
                    // 异步更新任务
                    asyncUpdateCache(key, loader);
                }
                return value;
            } finally {
                redisTemplate.delete(lockKey);
            }
        } else {
            // 其他线程等待100ms后重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return getHotData(key, loader);
        }
    }
    
    // 异步更新缓存
    @Async
    public void asyncUpdateCache(String key, Function<String, String> loader) {
        while (true) {
            try {
                Thread.sleep(60000); // 每分钟更新一次
                String value=loader.apply(key);
                if (value != null) {
                    redisTemplate.opsForValue().set(key, value);
                }
            } catch (Exception e) {
                log.error("异步更新缓存失败,key: {}", key, e);
            }
        }
    }
}
5. 接口签名验证(防止接口被恶意调用)
java 复制代码
// 技术亮点:时间戳防重放 + 非对称加密签名 + 设备指纹
@Component
public class ApiSignInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取请求参数
        String timestamp=request.getHeader("X-Timestamp");
        String sign=request.getHeader("X-Sign");
        String deviceId=request.getHeader("X-Device-Id");
        
        // 2. 时间戳校验:防止重放攻击(有效期5分钟)
        long now=System.currentTimeMillis();
        if (Math.abs(now-Long.parseLong(timestamp))>300000) {
            response.setStatus(401);
            return false;
        }
        
        // 3. 签名校验
        String signStr=deviceId+timestamp+request.getRequestURI();
        String serverSign=DigestUtils.md5DigestAsHex((signStr+"secret_key").getBytes());
        
        if (!serverSign.equals(sign)) {
            response.setStatus(401);
            return false;
        }
        
        return true;
    }
}

🎯 技术难点与解决方案对照表

技术难点 问题描述 解决方案 技术亮点
服务雪崩效应 一个非核心服务被打垮,导致整个调用链崩溃,核心服务也不可用 线程池隔离 + 熔断降级 每个服务使用独立线程池,异常时自动熔断并返回降级数据
IP 封禁误杀问题 简单的 IP 封禁会误杀共享 IP 的正常用户(如公司内网、校园网) 基于用户行为的风控 + 分级限流 不直接封 IP,而是对异常行为的用户进行限流;同一 IP 下不同用户独立计数
缓存击穿 / 雪崩 / 穿透 热点 key 过期导致数据库瞬间被打垮;大量 key 同时过期导致雪崩;恶意请求不存在的 key 导致穿透 击穿:互斥锁 + 热点 key 永不过期雪崩:过期时间随机化穿透:布隆过滤器 + 空值缓存 布隆过滤器提前拦截不存在的 key;空值缓存设置短过期时间
分布式限流一致性 单机限流无法控制集群总流量;分布式限流存在性能瓶颈 Sentinel 集群限流 + 令牌桶算法 令牌桶算法实现平滑限流;集群限流使用 Redis 作为令牌中心
人机验证被绕过 简单的验证码容易被 AI 识别;滑块验证容易被脚本模拟 行为式验证码 + 设备指纹 + 风控评分 分析用户鼠标轨迹、键盘输入速度等行为特征;结合设备指纹识别恶意设备
DDoS 攻击防护 大流量 DDoS 攻击直接打满带宽,源站无法响应 高防 IP + Anycast 流量清洗 + 隐藏源站 IP 流量先经过高防机房清洗,只把正常流量转发到源站;源站 IP 不对外暴露
数据库压力过大 大量写请求导致数据库 CPU/IO 飙升,响应变慢 读写分离 + 分库分表 + 异步写入 读请求走从库;大表分库分表;非实时写请求先写入 MQ,异步消费到数据库

真实面试模拟

真实面试模拟

面试官 😊:

欢迎,今天咱们聊个场景题。假设咱们公司有个 PC 版网站,最近被黑产用脚本狂刷接口------有爬数据的、扫号的、恶意提交的。你作为 Java 研发负责人,会怎么设计一套防护方案?

候选人 🧐:

我会搞一个纵深防御,从外到内层层过滤,不能指望单点解决。大概是这样:

text 复制代码
用户 → CDN/WAF 边缘 → Nginx/网关 → Java 应用 → 数据库
       ▲               ▲            ▲
   ① 脏流量丢弃    ② 令牌桶限流   ③ 业务风控+异步削峰

先把脏东西挡在门外,到了 Java 层只处理相对干净的流量。

面试官 👍:

思路不错。那先从边缘层说起,你会做什么?

候选人 💻:

这一层不是 Java 的活,但研发得推动运维去配。我主要做三件事:

  • CDN + WAF 速率限制:比如阿里云WAF,设置单 IP 每秒超过 20 次就弹出 JS 挑战,直接过滤掉脚本。
  • IP 信誉库:接入威胁情报,把已知的代理、IDC 机房 IP 直接拉黑。
  • 地域封禁:如果业务仅服务国内,就把海外流量全部拒绝。

这些能砍掉 80% 的低级攻击。

面试官 🚪:

接下来流量进到网关层了,你会怎么限流?

候选人:

我用 Redis + Lua 令牌桶,在 Spring Cloud Gateway 写个全局过滤器。

关键点有三个:

  • 粒度要细 :不能只限 IP,得限 IP + 接口路径,甚至 IP + 用户ID
  • 允许突发:令牌桶比漏桶友好,能应对正常流量毛刺。
  • 原子性:用 Lua 脚本保证判断和扣减在 Redis 里一次完成。

像这样一段 Lua:

lua 复制代码
-- 每次请求扣1个令牌,不够就返回0限流
local tokens = redis.call('HGET', KEYS[1], 'tokens')
tokens = tonumber(tokens or capacity)
if tokens >= 1 then
    redis.call('HSET', KEYS[1], 'tokens', tokens - 1)
    return 1
else
    return 0
end

超限就回 429,并且同一个 IP 连续超限三次,我直接把它扔进临时黑名单,冷置 5 分钟。

面试官 🤔:

很好。如果攻击者搞到了成千上万的家宽 IP,你的 IP 限流不就穿了?

候选人 🛡️:

确实,这时候就得从 IP维度 升级到 行为 + 账号维度

我会这样做:

  • 设备指纹:前端采集 Canvas、WebGL 等生成唯一设备 ID,对这个 ID 限流。他换 IP 也没用,除非连设备都换了。
  • 业务硬限制:比如发短信接口,同一个手机号 1 分钟只能发 1 次,一天最多 5 次。这是铁律,换 IP 也没法绕过。
  • 节奏分析:真人点击间隔是波动的,脚本是机械的。我可以在后台统计请求间隔的方差,方差极小就判定为脚本,直接踢挑战。

这样就算 IP 池再大,也躲不开行为和账号的约束。

面试官 🧱:

好,那进到 Java 应用层,你具体有哪些防御手段?

候选人 💪:

我总结为"三板斧":

手段 场景 实现
验证码 登录/注册/发短信等关键接口 触发限流后弹出滑块验证码,后端校验 ticket。
前端动态签名 防止脚本直接调接口 页面计算 timestamp+nonce+sign,后端校验时效和签名。
业务锁 同一用户重复操作 Redis SETNXuserId+action 加 5 秒锁。

验证码是人与机器的分流器,业务锁是防止误操作或恶意重复提交。

面试官 🚨:

那你怎么做到区分可疑等级,而不是一刀切?

候选人 ⚡:

我搞一个风险评分 + 分级处罚模型。在过滤器里实时打分:

  • 同一 IP 频繁切换 User-Agent → +20 分
  • 请求间隔高度一致(机器人特征)→ +15 分
  • 总在凌晨搞事情 → +10 分

然后按分数做动作:

风险等级 分数 动作
🟢 安全 0-30 正常通行
🟡 可疑 31-59 弹出验证码
🟠 危险 60-79 返回降级数据(缓存/空列表)
🔴 高危 80+ 临时封禁,并异步告警

这样既能拦截机器人,又不会误伤正常用户。

面试官 📈:

最后,针对刷接口导致数据库压力过大,你有什么办法?

候选人 📬:

对于可以异步处理的操作,比如发邮件、写日志、生成报表,我绝不会让他直接怼数据库。而是:

  1. 扔消息队列(RocketMQ / Kafka),请求先入队。
  2. 后端限速消费,比如每秒处理 50 条。
  3. 如果队列积压超过阈值,前置层直接拒绝新请求,提示"系统繁忙"。

这就叫削峰填谷,保护数据库不被冲垮。

面试官 👏:

整个体系搭完后,你怎么知道它起效了?

候选人 📊:

必须有监控闭环:

  • 埋点日志 :所有拦截、限流都打日志,带 requestId 方便串联。
  • 监控大盘:Prometheus + Grafana 显示 QPS、限流触发率、封禁 IP 数。
  • 自动联动:当某接口限流触发率超过 60%,自动告警,并通知运维调整 WAF 策略,把攻击 IP 段黑洞路由。

这样就不是被动挨打,而是自动免疫。

面试官 💻:

刚才你提到了 Redis Lua 令牌桶、前端动态签名、风险评分拦截器,这些概念不错。能不能挑一段你觉得最有技术亮点的代码,现场手写一下核心逻辑?不用完整,关键部分就行。

候选人 ⌨️:

没问题,我写三段,分别对应网关、应用、风控三层。

1. Redis Lua 令牌桶(网关层限流)

java 复制代码
// Java侧调用
public boolean tryAcquire(String key, double rate, int capacity) {
    String script = 
        "local tokens = redis.call('HGET', KEYS[1], 'tokens') " +
        "tokens = tonumber(tokens or ARGV[2]) " +   // 第一次初始化
        "if tokens >= tonumber(ARGV[3]) then " +
        "    redis.call('HSET', KEYS[1], 'tokens', tokens - tonumber(ARGV[3])) " +
        "    redis.call('EXPIRE', KEYS[1], ARGV[4]) " +
        "    return 1 " +
        "else " +
        "    return 0 " +
        "end";
    
    List<String> keys = Collections.singletonList("rate:" + key);
    List<String> args = Arrays.asList(
        String.valueOf(rate),          // ARGV[1]: 速率(令牌/秒)
        String.valueOf(capacity),      // ARGV[2]: 桶容量
        "1",                           // ARGV[3]: 请求消耗令牌数
        "10"                           // ARGV[4]: key过期时间(秒)
    );
    
    Long result = redisTemplate.execute(
        new DefaultRedisScript<>(script, Long.class), keys, args.toArray()
    );
    return result == 1;
}

亮点

  • 用 Lua 把"判断+扣减"打成原子操作,避免并发超发。
  • 支持动态速率、动态容量,不同接口从配置中心拉参数。
  • 通过 EXPIRE 给 key 设存活,内存不膨胀。

2. 前端动态签名校验(应用层防脚本直接调接口)

前端(伪代码):

javascript 复制代码
const timestamp = Date.now();
const nonce = generateUUID();
const params = { userId: 123, action: 'submit' };
const sign = md5(timestamp + nonce + JSON.stringify(params) + SECRET);

Java 后端校验:

java 复制代码
public boolean checkSign(HttpServletRequest request) {
    String timestamp = request.getHeader("X-Timestamp");
    String nonce = request.getHeader("X-Nonce");
    String sign = request.getHeader("X-Sign");
    String body = ...; // 读取请求体

    // 1. 时效性检查(60秒过期)
    if (Math.abs(System.currentTimeMillis() - Long.parseLong(timestamp)) > 60000) {
        return false;
    }
    
    // 2. Nonce去重(Redis Set判重)
    String nonceKey = "nonce:" + nonce;
    if (Boolean.FALSE.equals(redisTemplate.opsForValue().setIfAbsent(nonceKey, "1", 60, TimeUnit.SECONDS))) {
        return false; // 重放攻击
    }
    
    // 3. 签名比对
    String expected = DigestUtils.md5Hex(timestamp + nonce + body + SECRET);
    return expected.equals(sign);
}

亮点

  • 时间戳防"旧请求复用",Nonce 防"单次重放",签名防"参数篡改"。
  • Nonce 用 Redis SETNX 加过期,O(1) 极速去重,不存全量。

3. 风险评分拦截器(应用层隐形风控)

java 复制代码
@Component
public class RiskInterceptor implements HandlerInterceptor {
    
    @Autowired
    private RedisTemplate<String, Object> redis;
    
    @Override
    public boolean preHandle(HttpServletRequest request, ...) {
        String ip = getClientIp(request);
        String deviceId = request.getHeader("X-Device-Id");
        String userAgent = request.getHeader("User-Agent");
        
        int score = 0;
        
        // 规则1:同一IP频繁换UA(疑似代理池)
        String lastUA = (String) redis.opsForValue().get("ua:" + ip);
        if (lastUA != null && !lastUA.equals(userAgent)) {
            score += 20;
        }
        redis.opsForValue().set("ua:" + ip, userAgent, 10, TimeUnit.MINUTES);
        
        // 规则2:设备指纹异常(短时间内多个IP)
        if (deviceId != null) {
            Long ipCount = redis.opsForSet().size("device_ips:" + deviceId);
            if (ipCount > 5) score += 15;
            redis.opsForSet().add("device_ips:" + deviceId, ip);
            redis.expire("device_ips:" + deviceId, 10, TimeUnit.MINUTES);
        }
        
        // 根据分数做动作(伪代码)
        if (score > 30) {
            // 弹出验证码
            return challenge(request, response);
        } else if (score > 60) {
            // 临时封禁
            return ban(request, response);
        }
        return true;
    }
}

亮点

  • 利用 Redis 存储短期行为特征,内存计算,几乎无延迟。
  • 多维度叠加评分,越界即拦截,形成自愈式防御

面试官 🔥:

代码功底不错。那我们再深挖一下,这个防刷场景会有哪些技术难点?你怎么解决的?

候选人 🧗:

我总结五大难点和对应解法:

难点 为什么难 解决方案
🔴 分布式下限流一致性 多实例网关各自限流,会出现总量超发 ✅ 使用 Redis 中心化计数 + Lua 原子脚本。所有实例共用一个令牌桶,保证全局精准。
🟠 IP 代理池绕过 攻击者拥有海量家庭IP,IP限流失效 ✅ 升级到 设备指纹 + 行为节奏分析。结合前端生成设备ID,后台统计请求间隔方差,低方差判定为脚本。
🟡 误伤正常用户 严格的规则可能拦截真实用户的突发操作(如双11抢购) ✅ 采用 柔性处罚梯度(验证码→降级→封禁)。使用令牌桶允许突发。引入白名单(VIP用户、内部IP)避免误杀。
🟢 实时风控的性能开销 每次请求都要查 Redis、算分数,可能拖慢响应 ✅ 用 管道化 + 本地缓存 优化。例如 IP 风险分本地缓存 1 秒,减少 Redis 交互。风险计算规则用轻量数据结构(Set/Hash)。
🔵 前端签名被逆向 JS 代码里的加密逻辑和 SECRET 有暴露风险 动态混淆 + 短期有效 。SECRET 定期刷新(如20分钟),由后端注入页面。JS 本身经混淆器处理,增加破解成本。配合设备指纹,即使签名被解,仍需过行为关。

特别是"分布式限流一致性"这个点,我画个对比图:

text 复制代码
❌ 单机限流:各自为政
[实例A] → 计数器A (限10/s)   总允许量 = A+B = 20/s
[实例B] → 计数器B (限10/s)   实际可能超过预期

✅ 中心化限流
[实例A] ↘
         → Redis 令牌桶 (总容量10/s)
[实例B] ↗
无论多少实例,全局速率一致

面试官 😄:

非常好,从代码落地到难点攻克都很扎实,既有全局视野又能抠细节。这个场景题就到这里,我们继续下个问题。


公众号"Rain的Java大神之路"

个人博客"www.javadashen.com"

相关推荐
Rain的Java大神实战圈1 天前
接口被狂刷怎么处理
场景设计题
Rain的Java大神实战圈2 天前
线上在Docker中跑MySQL会怎样
场景设计题
Rain的Java大神实战圈3 天前
Docker搭建Redis集群完全指南
场景设计题
Rain的Java大神实战圈6 天前
短信接口被狂刷怎么处理
场景设计题
Rain的Java大神实战圈7 天前
如何避免订单重复提交
场景设计题
Rain的Java大神实战圈8 天前
MySQL误删数据如何恢复
场景设计题
Rain的Java大神实战圈9 天前
介绍一下分布式事务
场景设计题
Rain的Java大神实战圈10 天前
频繁FullGC如何优化
场景设计题
Rain的Java大神实战圈14 天前
MQ重复消费问题怎么解决
场景设计题