Skip to content

认证鉴权(Sa-Token) ​

ArchForge 使用 Sa-Token 1.45.0 做认证,不是 JWT,也不是 Spring Security。

两套相互独立的登录类型,会话保存在 Redis:

应用工具类Sa-Token type端口
管理端StpAdminUtil"admin":8080
C 端StpWebUtil"web":8081

登录流程(管理端) ​

┌──────────┐                  ┌──────────────┐                ┌──────────┐
│ Frontend  │                  │ LoginController│               │ TokenService│
└────┬─────┘                  └──────┬───────┘                └────┬─────┘
     │  POST /admin/auth/login       │                              │
     │  {username, password, code}   │                              │
     ├──────────────────────────────►│                              │
     │                               │  Verify captcha              │
     │                               │  Decrypt RSA password        │
     │                               │  BCrypt verify               │
     │                               │  StpAdminUtil.login(userId)  │
     │                               │                              │
     │  {accessToken, refreshToken,  │                              │
     │   expires, roles, perms}      │                              │
     │◄──────────────────────────────┤                              │
     │                               │                              │
     │  Subsequent requests:         │                              │
     │  Authorization: Bearer <token>│                              │
     └──────────────────────────────►│                              │

登录有限流:@RateLimit(key = "login", time = 60, maxCount = 5, limitType = IP) —— 每个 IP 每分钟最多 5 次。

令牌管理 ​

Sa-Token 签发的是 UUID 访问令牌(token-style: uuid),不是签名后的 JWT。

访问令牌 ​

  • 由 StpAdminUtil.login(userId) / StpWebUtil.login(userId) 创建
  • 存放在 Sa-Token 会话(Redis)
  • 请求头:Authorization: Bearer <token>
  • 默认有效期:7 天(sa-token.timeout: 604800)

刷新令牌(管理端) ​

  • 接口:POST /admin/auth/refresh-token
  • 随机 UUID 存在 Redis,映射到用户 ID
  • 返回新的 Sa-Token 访问令牌以及新的刷新令牌

配置 ​

yaml
sa-token:
  token-name: Authorization
  token-prefix: Bearer
  timeout: 604800
  active-timeout: -1
  is-concurrent: true
  is-share: false
  is-read-cookie: false
  token-style: uuid
  is-log: false
  is-print: false

生产环境密钥 没有默认值。Redis、数据源密码、RSA 密钥等都需要自行配置,不要把空的生产凭据提交进仓库。

授权 ​

控制器使用带 type 的 Sa-Token 注解:

java
@SaCheckLogin(type = StpAdminUtil.TYPE)
@SaCheckPermission(value = "system:user:add", type = StpAdminUtil.TYPE)
@PostMapping
public void addUser(...) { }

权限字符串来自 sys_menu.permission,例如 system:user:add、system:role:query、system:scheduler-job:list。

获取当前管理端用户:

java
SystemLoginUser loginUser = LoginContext.getAdminUser();

密码加密 ​

仍然是双层:

  1. RSA —— 登录密码的传输加密:RSA-OAEP / SHA-256。管理端登录页从 GET /admin/auth/getConfig 取 rsaPublicKey(由 arch-forge.rsa-private-key 推导),用浏览器自带的 WebCrypto 加密,不需要额外的库。prod 下解不开的密码按"用户名或密码错误"处理(10101),并计入登录失败锁定;其他 profile 回退为按原文处理。没有安全上下文时(非 localhost 的纯 HTTP),浏览器没有 WebCrypto,会发送明文——prod 请用 HTTPS。
  2. BCrypt —— 数据库中存储哈希

公开接口(管理端) ​

无需登录。名单即 AdminSaTokenConfig.PUBLIC_PATHS;若某个 /admin/auth/** 条目背后没有真实端点,契约测试会失败——不会给尚不存在的端点预先开放免登录:

  • POST /admin/auth/login、POST /admin/auth/refresh-token
  • GET /admin/auth/captchaImage、GET /admin/auth/getConfig
  • GET /livez、GET /readyz、GET /actuator/health/**、/actuator/info、/actuator/prometheus——staging / prod 下 actuator 本身在管理端口上(见配置)
  • GET /swagger-ui/** 与 GET /v3/api-docs/**(生产环境关闭)

其余接口都需要登录,并且每个管理端处理器都有自己的 @SaCheckPermission(只有登录/会话接口例外)。两条契约测试守着它:处理器缺权限会失败;权限码没有任何菜单或按钮授予也会失败——那样的权限只有 * 超级管理员能用。

验证码 ​

yaml
arch-forge:
  captcha:
    enabled: true
  captcha-type: math     # "math" 或 "text"

图片由 Kaptcha 生成,临时存放在 Redis。

登录响应(管理端) ​

管理端 JSON 包装为 {code, message, data}。data 大致如下:

json
{
  "accessToken": "xxxxxxxx-uuid-style-token",
  "refreshToken": "hex-without-dashes",
  "expires": "2025/01/14 00:00:00",
  "username": "admin",
  "roles": ["admin"],
  "permissions": ["*:*:*"]
}

C 端(archforge-server-web)错误使用 RFC 9457 ProblemDetail,而不是上述信封。

XSS 过滤 ​

XssFilter 会清理 查询参数和请求头。跳过 multipart/*、application/octet-stream、application/pdf 与 image/*。JSON 请求体仍需控制器级校验。

API 请求签名 ​

敏感接口或第三方接入可使用 @ApiSign。拦截器校验:

Header说明
X-App-Key应用标识
X-Timestamp请求时间戳(毫秒)
X-Nonce一次性随机串(防重放)
X-Sign小写十六进制 HMAC-SHA256 签名
HMAC-SHA256(appSecret, appKey + timestamp + nonce + body)
yaml
arch-forge:
  security:
    sign:
      enabled: true
      timeoutSeconds: 300
      nonceTtlSeconds: 600
      apps:
        test-app: test-secret

幂等 Token ​

@Idempotent 防止重复提交:

  • PARAM:方法签名 + 参数哈希生成 Redis SETNX 锁
  • TOKEN:客户端调用 GET /idempotent/token,再通过 X-Idempotent-Token 提交
  • HEADER:基于指定请求头值加锁

相关页面 ​

Released under the MIT License.