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 /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 /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:addsystem:role:querymonitor:job:list

获取当前管理端用户:

java
SystemLoginUser loginUser = LoginContext.getAdminUser();

密码加密

仍然是双层:

  1. RSA —— 前端用公钥加密密码,后端用 arch-forge.rsa-private-key 解密
  2. BCrypt —— 数据库中存储哈希

公开接口(管理端)

通常无需登录:

  • POST /auth/login
  • GET /auth/captchaImage
  • GET /auth/getConfig
  • GET /actuator/**(随 Profile 变化)
  • GET /swagger-ui/**GET /v3/api-docs/**(生产环境关闭)

验证码

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-streamapplication/pdfimage/*。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.