搜索 K
Appearance
Appearance
forge 的鉴权能力被拆成「核心契约」与「具体实现」两层:核心包 forge-auth 只定义接口与流程,真正签发、校验令牌的活儿交给 AuthProvider 的实现。框架内置两套实现,你引入哪个 jar,就用哪套实现,无需改一行代码。
本篇以宠物商店为背景,演示如何在 SpringSecurity(无状态 JWT)与 Sa-Token 两套方案之间切换,以及切换时需要注意的配置差异。涉及「为什么这样设计」的部分,请移步设计篇 鉴权实现的 SPI 选择。
| 维度 | forge-auth-spring-security | forge-auth-sa-token |
|---|---|---|
| 底层依赖 | JJWT(0.12.6)+ spring-boot-starter-security | sa-token-spring-boot3-starter(1.39.0)+ sa-token-jwt |
| 令牌形态 | 纯无状态 HS256 JWT | Sa-Token 令牌,有状态/无状态可切换 |
LoginUser 来源 | 解析 JWT 声明回填 | 凭 userId 回源数据库重建 |
| 主动失效 | 不支持(invalidate 空实现) | 支持(StpUtil.logoutByTokenValue) |
| JWT 编解码 | 下沉到 ForgeJwtTokenCodec,独立持有密钥 | 由 Sa-Token 内部管理 |
怎么选
追求纯无状态、轻量、与 Spring Security 原生集成,选 spring-security 实现;需要主动踢人下线、后续要用权限注解等更多能力,选 sa-token 实现。
无论选哪套实现,forge-auth 都是必须的,它提供 AuthProvider 契约、@Public 注解、拦截器与默认管理员初始化等基础设施。
<dependency>
<groupId>cn.cvking.forge</groupId>
<artifactId>forge-auth</artifactId>
</dependency>必须恰好引一个
两套实现都通过 SPI 贡献 AuthProvider。框架在启动时强制要求「有且仅有一个」实现,引零个或引两个都会 fail-fast 启动失败(详见文末反例)。
<dependency>
<groupId>cn.cvking.forge</groupId>
<artifactId>forge-auth-spring-security</artifactId>
</dependency><dependency>
<groupId>cn.cvking.forge</groupId>
<artifactId>forge-auth-sa-token</artifactId>
</dependency>引入即生效——这两个实现分别被标注为 @SPI.Service("spring-security") 与 @SPI.Service("sa-token"),框架通过 Spider.getAllExtensions(AuthProvider.class) 把当前 classpath 里的唯一实现挑出来使用。
通用配置键对两套实现都生效;forge.auth.jwt.* 一节主要供 spring-security 实现使用。
forge:
auth:
enabled: true # 是否启用鉴权,默认 true
tokenHeader: "Authorization" # 令牌请求头,默认 Authorization
tokenPrefix: "Bearer " # 令牌前缀,默认 "Bearer "
defaultAdminUsername: "admin" # 默认管理员用户名
defaultAdminPassword: "admin" # 默认管理员密码
white-list: # 额外放行的白名单路径
- /v1/api/petshop/public/**
jwt: # 仅 spring-security 实现消费
secret: "your-production-256bit-secret-key-here-32bytes-minimum"
expireMinutes: 720 # 令牌有效期(分钟),默认 12 小时生产环境务必覆盖 JWT 密钥
spring-security 实现的 forge.auth.jwt.secret 默认值是内置占位密钥 forge-auth-default-secret-please-change-it-in-production-env。HS256 要求密钥长度不小于 32 字节,生产环境必须自行覆盖为足够长的随机串。
切换实现的动作非常克制——只换 jar,不动代码、不动业务配置:
pom.xml 移除当前实现包;由于宠物商店的登录端点、@Public 放行、AuthUser/AuthRole 等 RBAC 模型都定义在 forge-auth 核心包,切换实现后这些都不受影响。例如给宠物列表加个免登录的「在售统计」端点,无论用哪套实现,写法都一样:
@RestController
@RequestMapping("/v1/api/petshop/pet")
public class PetController {
@Public
@GetMapping("/stats")
public Map<String, Object> onSaleStats() {
// 标注 @Public 后无需携带令牌即可访问
return petService.countByStatus();
}
}在 application.yml 打开鉴权模块的 debug 日志:
logging:
level:
cn.cvking.forge.auth: debug启动应用,控制台会打印当前选中的鉴权实现。引入 spring-security 实现时:
[auth] 使用鉴权实现: spring-security
[auth] 已创建默认管理员账号: admin
[auth] 默认管理员初始化完成,耗时 18ms若使用的是内置默认 JWT 密钥,spring-security 实现还会额外告警:
[auth] 正在使用内置默认 JWT 密钥,生产环境务必通过 forge.auth.jwt.secret 覆盖!切换成 Sa-Token 实现后重新启动,第一行随之变化,其余初始化日志一致:
[auth] 使用鉴权实现: sa-token
[auth] 已创建默认管理员账号: admin
[auth] 默认管理员初始化完成,耗时 21ms接着用默认管理员登录验证链路(端点带 /v1 前缀):
curl -X POST http://localhost:8080/v1/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}'拿到返回的 token 后,带上请求头访问受保护的宠物接口:
curl http://localhost:8080/v1/api/petshop/pet/1 \
-H "Authorization: Bearer <token>"令牌请求头可配置
上面的 Authorization 头与 Bearer 前缀均取自 forge.auth.tokenHeader 与 forge.auth.tokenPrefix,两套实现共用同一套配置。
现象:应用启动即抛 IllegalStateException,提示检测到多个鉴权实现同时存在。
原因:forge-auth-spring-security 与 forge-auth-sa-token 都通过 SPI 贡献 AuthProvider,框架在 Spider.getAllExtensions(AuthProvider.class) 后发现实现数大于 1,触发 fail-fast。常见于子模块各引一个、最终聚合到同一个可执行 jar。
修复:检查依赖树(mvn dependency:tree),确保最终 classpath 里只保留一套实现包,移除多余的那个。
现象:启动抛 IllegalStateException,提示未检测到任何鉴权实现。
原因:只引了核心包 forge-auth,没有引入任何 AuthProvider 实现,而 forge.auth.enabled 仍为默认的 true。
修复:按第二步二选一引入 forge-auth-spring-security 或 forge-auth-sa-token。
现象:使用 spring-security 实现时,初始化 ForgeJwtTokenCodec 阶段报错,提示密钥长度不足。
原因:HS256 经由 Keys.hmacShaKeyFor 做 fail-fast 校验,要求密钥长度不小于 32 字节。forge.auth.jwt.secret 配成了短串。
修复:把 forge.auth.jwt.secret 改为不少于 32 字节的随机串,切勿沿用内置默认占位密钥。
现象:明明配了 forge.auth.jwt.secret,启动却仍打印「正在使用内置默认 JWT 密钥」告警。
原因:常见于把 expireMinutes 误写成 expire-minutes 之外的形式、或把 jwt 一节错放到 forge.auth 之外的层级,导致绑定失败回落默认值。
修复:对照第三步的配置树逐层核对,secret、expireMinutes 必须在 forge.auth.jwt 节点下;white-list 在 forge.auth 节点下。
想了解两套实现背后的 SPI 一元性、JWT codec 下沉到 spring-security 实现等设计取舍,参见 鉴权实现的 SPI 选择(设计篇);SPI 与包扫描的通用机制见 前置知识。