Skip to content

鉴权快速开始

本篇带你在宠物商店项目里跑通一条最短的鉴权链路:引入 forge-auth 与任意一种实现 jar、配置 forge.auth.*、调用登录端点拿到 token,再带着 token 访问受保护接口。全程以可直接复制的代码为主,每个关键步骤后给出控制台真实日志作为「验证结果」。

关于「为什么这么设计」

本篇只讲怎么用。SPI 双实现的选择机制、JWT codec 下沉、白名单聚合等内部设计请看 鉴权模块设计。涉及包扫描与启动钩子的底层机制可参考 前置知识

第一步:引入依赖

鉴权模块拆成「核心契约包」与「具体实现包」两层。核心包 forge-auth 必须引入,实现包从下面两者中二选一(恰好一个,不能同时引)。

必须恰好一个实现

框架在启动时通过 SPI 扫描鉴权实现,要求有且仅有一个。一个都没有或同时引入两个,启动都会直接失败(fail-fast)。详见文末「常见反例与排查」。

核心包:

xml
<dependency>
    <groupId>cn.cvking.forge</groupId>
    <artifactId>forge-auth</artifactId>
</dependency>

实现包二选一。方案一,基于 JJWT 的无状态 JWT:

xml
<dependency>
    <groupId>cn.cvking.forge</groupId>
    <artifactId>forge-auth-spring-security</artifactId>
</dependency>

方案二,基于 Sa-Token:

xml
<dependency>
    <groupId>cn.cvking.forge</groupId>
    <artifactId>forge-auth-sa-token</artifactId>
</dependency>

两种实现共用同一套登录端点、同一套配置键、同一套 RBAC 模型,业务代码无需感知差异。它们的特点对比与取舍见 鉴权模块设计

第二步:配置 forge.auth.*

application.yml 里写入鉴权配置。最少可以全部走默认值,下面给出一份显式配置便于理解每个键的含义。

yaml
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 日志:

yaml
logging:
  level:
    cn.cvking.forge.auth: debug

第三步:启动应用,确认初始化

直接启动应用即可,无需手动建账号。框架在应用完全就绪后(ApplicationReadyEvent)会自动播种一个默认管理员 admin/admin 并绑定 SUPER_ADMIN 角色,且是幂等的——重启不会重复创建。

启动控制台会先打印选用了哪种鉴权实现,再打印默认管理员的初始化结果:

text
[auth] 使用鉴权实现: spring-security
[auth] 已创建默认管理员账号: admin
[auth] 默认管理员初始化完成,耗时 18ms

TIP

若你引入的是 Sa-Token 实现,第一行会变成 [auth] 使用鉴权实现: sa-token

如果你没有覆盖 forge.auth.jwt.secret,在选用 Spring Security 实现时还会看到这条告警,提示生产环境必须替换密钥:

text
[auth] 正在使用内置默认 JWT 密钥,生产环境务必通过 forge.auth.jwt.secret 覆盖!

第四步:登录拿 token

向登录端点提交用户名和密码。该端点是内置白名单,免登录即可访问。

bash
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),形如:

json
{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwidXNlcm5hbWUiOiJhZG1pbiI...",
  "user": {
    "userId": 1,
    "username": "admin",
    "nickname": "admin",
    "superAdmin": true
  }
}
登录失败会返回什么

用户名或密码错误返回错误码 40110(LOGIN_FAILED,「用户名或密码错误」);账号被禁用返回 40111(USER_DISABLED,「账号已禁用」)。

第五步:带 token 访问受保护接口

把上一步拿到的 token 放进请求头 Authorization,前缀 Bearer ,访问需要登录的接口。以「查询当前登录用户」为例:

bash
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),免登录访问:

java
@Operation(summary = "在售宠物列表")
@Public
@GetMapping("/v1/api/pet/on-sale")
public List<Pet> listOnSale() {
    // 游客无需携带 token 即可访问
    return petService.listOnSale();
}

方式二,在 application.ymlforge.auth.white-list 里按路径放行:

yaml
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-securityforge-auth-sa-token)。框架要求恰好一个实现,零个即 fail-fast。 修复:在两个实现包中二选一引入。

反例二:同时引了两个实现,启动直接失败

现象:启动报错,提示「检测到多个鉴权实现同时存在」。 原因:forge-auth-spring-securityforge-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 字节的字符串。务必同时留意启动时的默认密钥告警日志,生产环境不要沿用内置默认值。