1950 字
约 6 分钟
7
S06-B06 · Java 生态四库实测:JJWT / sensitive-word / tianai-captcha / Commons Lang3

适用读者:要在项目里接一个"标准能力"(令牌、敏感词、验证码、字符串工具)的人。
前置:S06-B05(第三方工具库)、B04(JDK 自带工具类)、S07(鉴权基础)。
覆盖旧文:3705(JJWT 入门)、3707(sensitive-word 入门)、3708(tianai-captcha 入门)、3709(Commons Lang3 入门)→ 4 篇 / 约 1.1 万字符。
扩展点:E-S06-015(四库的"该用/不该用"判据与选型替代)、E-S06-016(JJWT 版本迁移与三个安全坑)、E-S06-017(敏感词与验证码的工程约束:性能、绕过、可维护)。
下一篇:S06-J04 · Maven 深水区。


1. 一句话结论

"标准能力"优先用成熟库,但每个库都自带一条不可绕过的约束:JWT 的密钥强度与算法校验、敏感词的词库维护与误伤、验证码的绕过与无障碍、字符串工具的分包体积。先看约束,再接库。

出处:A-3705(JJWT)、A-3707(敏感词)、A-3708(验证码)、A-3709(Lang3)。


2. 四库总览与选型判据(E-S06-015)

库 解决 该用 不该用 替代
JJWT JWT 签发/解析(HS256/RS256) 无状态会话、服务间令牌 需要"可撤销"的会话(JWT 天然不可撤) 服务端 session/OAuth2 introspection
sensitive-word(houbb) 敏感词过滤(DFA/Trie) 评论、昵称、UGC 文本 需要语义级审核(反讽、变形) 内容安全 API + 规则引擎
tianai-captcha 行为/滑块验证码 登录、注册、风控前置 高频 API 防刷(应用限流) 限流 + 设备指纹
Commons Lang3 字符串/日期/反射/Builder 补齐 JDK 缺失的工具方法 已有 JDK 等价 API(String.isBlank)时 JDK 原生(17+ 覆盖多数场景)

判据:"JDK 已能做的不引库"——Java 17 后 String.isBlank/strip/repeat、List.of、Optional、records 已覆盖大量 Lang3 用途;引库前先查 JDK 是否有等价方法。


3. JJWT:版本迁移与三个安全坑(E-S06-016)

API 分水岭(0.9.x → 0.11+/0.12+):

// 0.9.x(旧,已不推荐)
String jwt = Jwts.builder().setSubject("u1").signWith(SignatureAlgorithm.HS256, secret).compact();

// 0.12.x(当前口径):显式 Key + 显式算法
SecretKey key = Keys.hmacShaKeyFor(secretBytes);          // ≥ 32 字节(HS256)
String jwt = Jwts.builder()
        .subject("u1")
        .issuedAt(new Date())
        .expiration(Date.from(Instant.now().plus(15, ChronoUnit.MINUTES)))
        .signWith(key, Jwts.SIG.HS256)
        .compact();

Claims claims = Jwts.parser().verifyWith(key).build()
        .parseSignedClaims(jwt).getPayload();

三个安全坑:

① 算法混淆攻击:解析时不限定算法 → 攻击者用 alg=none 或换算法伪造
   → 解析必须 verifyWith(key) 绑死算法,不读 token 头里的 alg 决定用什么
② 密钥太短/口令当密钥:HS256 要求 ≥256 bit(32 字节)
   → Keys.hmacShaKeyFor 会直接抛 WeakKeyException,别捕获后"凑合"
③ 把敏感信息放进 payload:JWT 只是 base64url 编码,不是加密
   → 只放 sub/exp/scope 等非敏感声明;需要加密用 JWE

有效期与撤销:exp 只能"到期失效",不能"立即吊销"。要吊销 → 维护短名单(Redis 存 jti,TTL=剩余有效期)或改用不透明 token + 服务端校验。

版本口径:JJWT 0.12.x 的 Jwts.SIG.*、verifyWith、parseSignedClaims 与 0.11.x 略有差异,按所用版本核对官方 README(待核具体小版本)。


4. sensitive-word:DFA 与误伤控制(E-S06-017)

SensitiveWordBs bs = SensitiveWordBs.newInstance()
        .ignoreCase(true).ignoreWidth(true)        // 全角/半角、大小写
        .ignoreNumStyle(true)                      // 1 vs 一
        .wordAllow(wordAllowList)                  // 白名单(避免误伤)
        .enableNumCheck(false).enableEmailCheck(false)  // 数字/邮箱不作为敏感片段
        .init();

boolean hit = bs.contains("文本");
String replaced = bs.replace("文本", '*');
工程约束 说明
词库维护 词库是资产:版本化、可热更新、有变更审计;不要硬编码在代码里
误伤 中文分词边界会导致"正常词被切出敏感片段"→ 白名单 + 词边界策略
绕过 拼音、拆字、符号插入("敏*感")、形近字 → 需归一化(全角/半角、去符号、繁简)后再匹配
性能 DFA 单次匹配近似 O(n);但每次新建实例会重建 Trie(昂贵)→ 全局单例 + 词库变更时重建
语义 规则库无法覆盖反讽/上下文 → 高风险场景叠加人工或第三方内容安全 API

判据:"过滤"不等于"审核"——过滤解决"明显违规词",审核需要模型与人工;两者组合,别指望词库兜底。


5. tianai-captcha:接入与三个边界

流程:前端请求 → 服务端生成(返回背景图/滑块参数 + 唯一 id)→ 用户交互
      → 前端回传轨迹 → 服务端校验(一次性、带过期)
边界 说明
一次性 校验通过即失效;重复提交必须拒绝(否则可重放)
过期 生成后 1~2 分钟过期;校验失败计数要限次
无障碍 滑块对键盘/读屏用户不友好 → 提供语音/短信备选
不是防刷主力 验证码挡"批量脚本",但挡不住"人工打码平台"→ 必须叠加 IP/设备/行为限流

判据:验证码的成本目标是"提高单次攻击成本",不是"零攻击"——把它放在限流之后,而不是替代限流。


6. Commons Lang3:什么时候还需要它

场景 Lang3 JDK 17 原生
判空 StringUtils.isBlank String.isBlank(但 null 不安全)
字符串拼接 StringUtils.join String.join(不支持 null 元素)
反射工具 FieldUtils/MethodUtils 无(需自己写或引库)
Builder EqualsBuilder/HashCodeBuilder record + Objects.equals/hash
日期 DateUtils java.time(更应优先用)

判据:Lang3 的剩余价值集中在"null 安全的字符串工具 + 反射 + Builder";日期一律用 java.time,不要再引 DateUtils。

体积:Lang3 约 600 KB(无传递依赖),Android/Serverless 场景需评估;可用 commons-lang3 的按需子集或自行封装 5 个方法替代。


7. 反例:六种"接了库反而更糟"

做法 后果
JJWT 解析时不绑算法 算法混淆伪造令牌
HS256 用 8 字节口令 弱密钥,可离线爆破
把手机号/身份证放 JWT payload base64 可解,等于明文
敏感词每次请求 new 实例 每请求重建 Trie,CPU 飙高
只靠词库过滤做内容审核 变形词与语义违规全部漏过
验证码替代限流 打码平台人工绕过,接口被打穿

8. 自测

  1. 四库各解决什么、各自的"不该用"?
  2. JJWT 0.12 的签发/解析最小代码?三个安全坑?
  3. JWT 为什么不能"立即吊销",怎么办?
  4. 敏感词的四条工程约束?为什么不能每次 new 实例?
  5. 验证码与限流的关系?
答案
  1. 令牌/敏感词/验证码/字符串工具;需可撤销会话、语义审核、防刷主力、JDK 已有等价 API 时不引。
  2. Keys.hmacShaKeyFor + signWith(key, Jwts.SIG.HS256);parser().verifyWith(key);算法混淆、弱密钥、敏感信息入 payload。
  3. 无状态且自校验;用 jti 黑名单(TTL=剩余有效期)或改不透明 token。
  4. 词库版本化、误伤白名单、绕过归一化、单例复用;重建 Trie 昂贵。
  5. 验证码在限流之后,是提高攻击成本而非替代限流。

9. 下一篇

S06-J04 · Maven 深水区 —— Profiles 与环境隔离、插件绑定与生命周期、依赖冲突的三种表现与 dependencyManagement 的正确用法、私服与制品可复现。


本篇属于系列《S06 · Java 工程化与组件》。旧文来源:A-3705、A-3707/3708、A-3709;扩展点 E-S06-015/022/023。库版本口径为 2026-11 时点,具体小版本 API 以官方文档为准(待核)。

S06-B06 · Java 生态四库实测:JJWT / sensitive-word / tianai-captcha / Commons Lang3
https://www.clxhxhhr.top/posts/5260/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。