Skip to content

feat(system/client): 添加双token认证配置 - #212

Closed
luoqiz wants to merge 1 commit into
continew-org:devfrom
luoqiz:feat-refresh-token
Closed

feat(system/client): 添加双token认证配置#212
luoqiz wants to merge 1 commit into
continew-org:devfrom
luoqiz:feat-refresh-token

Conversation

@luoqiz

@luoqiz luoqiz commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

PR 类型

  • 新 feature
  • Bug 修复
  • 功能增强
  • 文档变更
  • 代码样式变更
  • 重构
  • 性能改进
  • 单元测试
  • CI/CD
  • 其他

PR 目的

解决方案

PR 测试

Changelog

模块 Changelog Related issues

其他信息

提交前确认

  • PR 代码经过了完整测试,并且通过了代码规范检查
  • 已经完整填写 Changelog,并链接到了相关 issues
  • PR 代码将要提交到 dev 分支

Summary by CodeRabbit

  • 新功能
    • 增加刷新令牌机制,支持双令牌模式(访问令牌 + 刷新令牌)或单令牌模式返回
    • 新增 /auth/refreshToken 接口以使用刷新令牌换取新登录响应
    • 客户端配置新增“启用刷新令牌”和“刷新令牌超时”设置
    • 数据库初始化包含刷新令牌相关字段与默认种子数据

coderabbitai[bot]

This comment was marked as duplicate.

coderabbitai[bot]

This comment was marked as duplicate.

@luoqiz
luoqiz force-pushed the feat-refresh-token branch from c9285bd to f8e111f Compare April 24, 2026 05:05
coderabbitai[bot]

This comment was marked as duplicate.

coderabbitai[bot]

This comment was marked as duplicate.

@luoqiz
luoqiz force-pushed the feat-refresh-token branch from f8e111f to 3b0f6e7 Compare April 24, 2026 06:27
coderabbitai[bot]

This comment was marked as duplicate.

@luoqiz
luoqiz force-pushed the feat-refresh-token branch from 3b0f6e7 to 2a22d06 Compare April 24, 2026 06:40
@continew-org continew-org deleted a comment from coderabbitai Bot Aug 7, 2026
@Charles7c

Copy link
Copy Markdown
Member

整体评价:双 token/单 token 的响应模型用继承 + @SuperBuilder 实现,设计清晰;复用 SaTempUtil 做 refresh token、刷新后立即轮换(rotation)删除旧 token,方向正确。但刷新链路存在一个功能性 blocker 和几个安全问题,建议修复后再合入。

🔴 Blockers

1. 刷新后 access token 大概率立即失效(核心功能问题)
LoginHandler.buildLoginResploginParameter.setToken(StpUtil.getTokenValue()) 把 access token 写入了 loginParameter.token 字段并随 refresh token 一起持久化;而刷新时 StpUtil.login(userId, loginParameter)消费 loginParameter.token(Sa-Token 官方文档: setToken = 「预定此次登录生成的 Token」),导致:

  • 新 access token = 旧 access token(值相同)
  • 紧接着 StpUtil.kickoutByTokenValue(oldToken) 把刚创建的新会话踢下线 → 刷新返回的 token 立即 401

建议:旧 token 不要存放在 loginParameter.token 字段,改用 loginParameter.setExtra("oldToken", ...) 保存;刷新前先 loginParameter.setToken(null)(或 clone 一份干净参数)再登录。可先本地验证:刷新前后打印 access token 值是否相同。

2. 刷新时未校验账号/客户端状态,被禁用用户可无限续期
refreshToken 只校验了临时 token 有效,未校验:用户是否被禁用/删除、客户端是否仍启用、isEnableRefreshToken 是否仍为 true。被禁用用户只要持有未过期的 refresh token,就能一直续出新 token。建议刷新时重新校验 user、client 状态,不满足则抛业务异常并删除该 refresh token。

3. Refresh token 不绑定设备维度,多端/并发场景互相顶掉
SaTempUtil.createToken(userId, refreshExpiresIn, false) 第三个参数 isConcurrent=false,同一账号只允许一个 refresh token:

  • 客户端开启多端登录(isConcurrent=true)时,设备 B 登录会顶掉设备 A 的 refresh token,设备 A 下次刷新直接失败;
  • 同一 refresh token 并发刷新时,两个请求都 parse 成功、各自 createToken,后创建者顶掉先创建者,其中一个请求拿到的新 refresh token 立即失效。

建议 refresh token 至少绑定 userId + clientId(或设备维度),并做防重放处理(先删后建/原子校验)。

4. Token 通过 URL Query 传递 + @SaIgnore 匿名可调 + 无限流
@RequestParam 传 refresh token 会进入访问日志、网关日志、浏览器历史,泄露风险高。建议改 request body 或 header 传递;并参照登录接口补充限流/防刷。

🟡 Suggestions

  • UserContextHolder.getContext(userId) 在 account session 已被清理时返回 null,此时 setExtra(LOGIN_USER, null) 会导致后续 getContext() 空指针。建议判空并回退到 DB 重新加载用户上下文。
  • (SaLoginParameter) refreshTokenInfo 直接强转无类型防护,若同 userId 存在其他业务创建的临时 token 会抛 ClassCastException(500)。建议 instanceof 校验或存储自定义封装对象。
  • ClientResp 中新增的两个字段 @ExcelProperty(order = 7)timeout 重复,导出列顺序错乱,请改为 7/8 并顺延后续字段。
  • import java.util.*; 通配符导入(原为逐个导入),不符合 P3C 规范,且属于无关改动。
  • 无关 diff:AuthController.authorize 缩进、authType.getDescription() 换行缩进被改动,与本次功能无关,建议还原,保持 PR 聚焦。
  • 种子数据 INSERT INTO sys_client ... VALUES (2, ...) 硬编码主键,而该表为 AUTO_INCREMENT,若目标环境已有 id=2 数据会主键冲突导致 Liquibase 失败。建议参考现有种子数据写法做查重/ON DUPLICATE KEY
  • refreshToken 返回类型声明为 LoginResp(父类),Swagger 文档不会展示子类字段(accessToken/refreshToken 等),前端联调看不到。建议返回具体类型或用 @Schema(oneOf = ...) 标注。

💭 Nits

  • LoginHandler.buildLoginResp@return 注释缺描述;文件末尾缺换行。
  • @EqualsAndHashCode(callSuper = true) 在 lombok.config 已全局配置,子类显式声明冗余。
  • DoubleTokenLoginResp.accessExpiresIn example=1800 与 seed 数据 timeout=86400 不一致,示例易误导。
  • 建议先 SaTempUtil.deleteToken(refreshToken)kickoutByTokenValue(oldToken),缩小竞态窗口。

按优先级处理:先修 #1(不修功能直接不可用),再补 #2/#3 的安全与多端场景。整体设计思路没问题,完善后是个不错的 feature 👍

@sonarqubecloud

Copy link
Copy Markdown

@continew-org continew-org deleted a comment from sonarqubecloud Bot Aug 31, 2026
@Charles7c Charles7c closed this Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants