搜索 K
Appearance
Appearance
本篇带你在宠物商店项目里跑通一条最短的鉴权链路:引入 forge-auth 与任意一种实现 jar、配置 forge.auth.*、调用登录端点拿到 token,再带着 token 访问受保护接口。全程以可直接复制的代码为主,每个关键步骤后给出控制台真实日志作为「验证结果」。
鉴权模块拆成「核心契约包」与「具体实现包」两层。核心包 forge-auth 必须引入,实现包从下面两者中二选一(恰好一个,不能同时引)。
必须恰好一个实现
框架在启动时通过 SPI 扫描鉴权实现,要求有且仅有一个。一个都没有或同时引入两个,启动都会直接失败(fail-fast)。详见文末「常见反例与排查」。
核心包:
<dependency>
<groupId>cn.cvking.forge</groupId>
<artifactId>forge-auth</artifactId>
</dependency>实现包二选一。方案一,基于 JJWT 的无状态 JWT:
<dependency>
<groupId>cn.cvking.forge</groupId>
<artifactId>forge-auth-spring-security</artifactId>
</dependency>方案二,基于 Sa-Token:
<dependency>
<groupId>cn.cvking.forge</groupId>
<artifactId>forge-auth-sa-token</artifactId>
</dependency>两种实现共用同一套登录端点、同一套配置键、同一套 RBAC 模型,业务代码无需感知差异。它们的特点对比与取舍见 鉴权模块设计。
在 application.yml 里写入鉴权配置。最少可以全部走默认值,下面给出一份显式配置便于理解每个键的含义。
forge:
auth:
enabled: true # 是否启用,默认 true
tokenHeader: "Authorization" # 令牌所在请求头,默认 Authorization
tokenPrefix: "Bearer " # 令牌前缀,默认 "Bearer "
defaultAdminUsername: "admin" # 默认管理员用户名
defaultAdminPassword: "admin" # 默认管理员密码
white-list: # 额外放行的白名单路径
- /v1/api/public/**
jwt:
secret: "your-production-256bit-secret-key-here-32bytes-minimum" # HS256 密钥,至少 32 字节
expireMinutes: 720 # 令牌有效期(分钟),默认 12 小时生产环境务必覆盖 JWT 密钥
forge.auth.jwt.secret 若沿用内置默认值,启动时会打印告警日志。生产环境必须替换为自己的密钥,且长度不少于 32 字节(HS256 要求),否则签发时会直接抛异常。
为了看到本篇的「验证结果」,建议同时在 application.yml 里打开鉴权模块的 debug 日志:
logging:
level:
cn.cvking.forge.auth: debug直接启动应用即可,无需手动建账号。框架在应用完全就绪后(ApplicationReadyEvent)会自动播种一个默认管理员 admin/admin 并绑定 SUPER_ADMIN 角色,且是幂等的——重启不会重复创建。
启动控制台会先打印选用了哪种鉴权实现,再打印默认管理员的初始化结果:
[auth] 使用鉴权实现: spring-security
[auth] 已创建默认管理员账号: admin
[auth] 默认管理员初始化完成,耗时 18msTIP
若你引入的是 Sa-Token 实现,第一行会变成 [auth] 使用鉴权实现: sa-token。
如果你没有覆盖 forge.auth.jwt.secret,在选用 Spring Security 实现时还会看到这条告警,提示生产环境必须替换密钥:
[auth] 正在使用内置默认 JWT 密钥,生产环境务必通过 forge.auth.jwt.secret 覆盖!向登录端点提交用户名和密码。该端点是内置白名单,免登录即可访问。
curl -X POST http://localhost:8080/v1/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "admin"}'请求体对应 LoginRequest(username, password)。校验通过后,框架从数据库取出 AuthUser、用 BCrypt 比对密码、确认账号状态为 ENABLED,随后构建 LoginUser 并调用所选实现的 AuthProvider.issueToken() 签发令牌。
返回结果对应 LoginResult(token, user),形如:
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwidXNlcm5hbWUiOiJhZG1pbiI...",
"user": {
"userId": 1,
"username": "admin",
"nickname": "admin",
"superAdmin": true
}
}用户名或密码错误返回错误码 40110(LOGIN_FAILED,「用户名或密码错误」);账号被禁用返回 40111(USER_DISABLED,「账号已禁用」)。
把上一步拿到的 token 放进请求头 Authorization,前缀 Bearer ,访问需要登录的接口。以「查询当前登录用户」为例:
curl http://localhost:8080/v1/api/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwidXNlcm5hbWUiOiJhZG1pbiI..."拦截器 ForgeAuthInterceptor 会拦截 /v1/**、/v2/** 路径,从 Authorization 头按 tokenPrefix 去掉前缀取出令牌,调用 AuthManager.getLoginUser(token) 解析,并把当前用户写入 ForgeAuthContext(ThreadLocal),请求结束后自动清理。解析成功后即可正常返回当前用户信息。
业务接口里要拿当前登录用户,直接从 ForgeAuthContext 取即可,无需自己解析 token。
宠物商店的「商品列表」「分类查询」这类页面通常希望游客也能看。两种放行方式任选其一。
方式一,在 Controller 方法或类上加 @Public 注解(来自 cn.cvking.forge.auth.annotation.Public),免登录访问:
@Operation(summary = "在售宠物列表")
@Public
@GetMapping("/v1/api/pet/on-sale")
public List<Pet> listOnSale() {
// 游客无需携带 token 即可访问
return petService.listOnSale();
}方式二,在 application.yml 的 forge.auth.white-list 里按路径放行:
forge:
auth:
white-list:
- /v1/api/pet/on-sale
- /v1/api/category/**@Public 与 white-list 的区别
@Public 贴在代码上、随接口走,更易在阅读 Controller 时发现;white-list 走配置、适合按路径前缀批量放行。两种机制的聚合细节见 鉴权模块设计。
反例一:没引实现 jar,启动直接失败
现象:应用启动报错,提示「未检测到任何鉴权实现」。 原因:只引了核心包 forge-auth,没有引入任何 AuthProvider 实现(forge-auth-spring-security 或 forge-auth-sa-token)。框架要求恰好一个实现,零个即 fail-fast。 修复:在两个实现包中二选一引入。
反例二:同时引了两个实现,启动直接失败
现象:启动报错,提示「检测到多个鉴权实现同时存在」。 原因:forge-auth-spring-security 与 forge-auth-sa-token 被同时引入,SPI 扫描到两个实现无法决定用哪个。 修复:移除其中一个实现包,确保只保留一个。
反例三:访问受保护接口返回 40100
现象:调用 /v1/** 下的接口返回错误码 40100(TOKEN_MISSING,「未携带令牌,请先登录」)。 原因:请求没带 Authorization 头,或带了但前缀与 forge.auth.tokenPrefix 不一致(默认是 Bearer ,注意 Bearer 后有一个空格)。 修复:补上请求头 Authorization: Bearer <token>,确认前缀与配置一致;若该接口本就该公开,改用 @Public 或加入 white-list。
反例四:token 过期或被篡改返回 40101
现象:之前能用的 token 现在调用接口返回错误码 40101(TOKEN_INVALID,「令牌无效或已过期,请重新登录」)。 原因:令牌超过 forge.auth.jwt.expireMinutes 有效期,或令牌被改动、签名校验不通过。 修复:重新调用登录端点拿新 token;若需要更长的会话,调大 forge.auth.jwt.expireMinutes。
反例五:JWT 密钥太短,签发时抛异常
现象:登录时报错,密钥相关异常。 原因:forge.auth.jwt.secret 长度不足 32 字节,不满足 HS256 对密钥长度的要求。 修复:把密钥换成至少 32 字节的字符串。务必同时留意启动时的默认密钥告警日志,生产环境不要沿用内置默认值。