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 缓存击穿?

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

整理了面试真题、每日技术知识点、系统学习路线,√X搜「Rain的Java 大神之路」

每天拆一个知识点,陪你悄悄变强,有空来坐坐。


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

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

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] ↗
无论多少实例,全局速率一致
面试官 😄:

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


整理了面试真题、每日技术知识点、系统学习路线,√X搜「Rain的Java 大神之路」

每天拆一个知识点,陪你悄悄变强,有空来坐坐。

相关推荐
闻哥24 分钟前
小表驱动大表——SQL查询优化的核心原则(深度剖析+流程图)
数据库·sql·流程图
宸津-代码粉碎机31 分钟前
AI工程化高阶实战|从Demo到生产落地核心架构、全套源码与避坑指南
java·大数据·开发语言·人工智能·python·架构
cmes_love40 分钟前
港股Level2历史数据:逐笔成交与分钟线
数据库·oracle
自由能燃气设备40 分钟前
燃气热水锅炉/全预混低氮冷凝锅炉哪个品牌好?哪家好?6大商用品牌横评
大数据·数据库·人工智能
智购科技智能售货柜43 分钟前
2026自动售货机设备能耗计量系统:从功率监测到能效分析的工程实践~YH
数据库·人工智能·redis·缓存·架构·perl·symfony
云贝贝贝1 小时前
PostgreSQL 生产环境备份恢复与高可用实战
数据库·postgresql
程序员-Benothing1 小时前
MySQL 中的 MVCC 是什么?如果没有 MVCC,会有什么影响?
数据库·mysql
Zenova EdgeOS1 小时前
工业网关心跳机制:从 Keepalive 到健康判定的工程实战
大数据·网络·数据库·边缘计算·工业网关
疯狂打码的少年1 小时前
【数据库技术】关系完整性约束(实体/参照/用户定义)
运维·服务器·数据库·笔记