provider 凭证 ──▶ OAuthVerifyService.verify(provider, credential) ──▶ providerUid
│
┌───────────────────────────────────────────────────────┘
▼
查 info_user_oauth (provider, provider_uid)
├─ 命中 → 取 InfoUser → 校 status/del_flag → issueToken(provider) → 返回
└─ 未命中 → Redis 缓存 {provider,providerUid}(tempKey,TTL5m) → 返回 needPhone+tempKey
│
┌───────────────────────────────────────────────────────┘
▼ (前端引导输手机号 → getcode 发短信)
oauthBindPhone(tempKey, phone, code)
原子消费 tempKey + 取 providerUid + 验短信(复用 lodeing: redis code==input || "8888")
getuser(phone):
├─ 已注册 → 关联(写 oauth)
└─ 未注册 → createUser(phone) 新建 → 写 oauth
→ issueToken(provider) → 返回
JwtUtil.setToken(CacheConstants.USER_TOKEN_KEY, loginDto),仅给 LoginUserDto 加 provider 字段并在 setToken 写入 claim。createUser 的「建 InfoUser + 建钱包 + 删旧 token」逻辑;昵称=手机号不变。getcode 发送 + lodeing 的校验(key=phone 去+,万能码 8888)。tempKey 在绑定请求开始时以删除成功作为唯一消费权;任一后续校验失败都必须重新发起 OAuth 登录,避免重放和并发重复绑定。CREATE TABLE info_user_oauth (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL COMMENT '关联 info_user.user_id',
provider VARCHAR(16) NOT NULL COMMENT 'apple/google/line_user/line_rider/line_merchant',
provider_uid VARCHAR(64) NOT NULL COMMENT '三方稳定用户ID(Apple sub / Google sub / LINE userId)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_provider_uid (provider, provider_uid),
KEY idx_user_id (user_id)
) COMMENT='三方账号绑定';
POST /infouser/user/oauthLogin (@Anonymous)请求 OAuthLoginDto:
provider apple|google|line_user|line_rider|line_merchant
credential identityToken(Apple) / idToken(Google) / authorizationCode(LINE)
cid, cidType, deviceToken, voIPToken // 推送字段,与现有登录一致
响应(命中):
{ code:200, msg, data: InfoUser, token } // token claim provider=本次渠道
响应(未命中,首次):
{ code:200, msg, data:{ status:"needPhone", tempKey } }
POST /infouser/user/oauthBindPhone (@Anonymous)请求 OAuthBindDto:
tempKey, phone, code // code=短信验证码
cid, cidType, deviceToken, voIPToken
响应:{ code:200, msg, data: InfoUser, token }
| provider | 凭证 | 校验 | 取 providerUid | 依赖 |
|---|---|---|---|---|
| apple | identityToken(ES256 JWT) | 拉 appleid.apple.com/auth/keys(JWKS) 验签 + 校 iss/aud/exp |
sub |
nimbus-jose-jwt(新增) |
| ID-Token | GET oauth2.googleapis.com/tokeninfo?id_token= 取 claims + 校 aud=clientId |
sub |
httpclient4(已有) | |
| line_user / line_rider / line_merchant | authorization code | 按 provider 选择 Channel,POST 换 access_token,再 GET api.line.me/v2/profile(Bearer) |
userId |
httpclient4(已有) |
Google 走 tokeninfo HTTP 而非 firebase-admin,因后者需初始化 FirebaseApp+服务账号(项目未配置),tokeninfo 零配置即可。
| 类型 | 文件 |
|---|---|
| 新增实体 | ruoyi-system/.../domain/InfoUserOauth.java |
| 新增 mapper | ruoyi-system/.../mapper/InfoUserOauthMapper.java(BaseMapper,无 XML) |
| 新增校验服务 | ruoyi-admin/.../utils/oauth/OAuthVerifyService.java |
| 新增 DTO | ruoyi-admin/.../user/dto/OAuthLoginDto.java、OAuthBindDto.java |
| 改 controller | ruoyi-admin/.../user/InfoUserController.java(+2 方法) |
| 改 token | ruoyi-common/.../model/LoginUserDto.java、ruoyi-system/.../utils/JwtUtil.java |
| 改配置 | ruoyi-admin/src/main/resources/application.yml(oauth 段) |
| 改依赖 | ruoyi-admin/pom.xml(+nimbus-jose-jwt) |
| SQL | updatesql/sql.md |
GET /auth/line/callback,回调 URL 增加 provider=line_user|line_rider|line_merchant。OAuthVerifyService 按 provider 选择对应 Channel ID、Channel Secret 和 redirect URI;token/profile URL 继续共用。info_user_oauth.provider 直接保存三个 provider 值;现有 line 数据通过 SQL 迁移为 line_user。line_user 使用 phone 并保留首次自动创建;line_rider、line_merchant 使用 tel_phone 且只绑定已有账号。USER_TOKEN_KEY、QS_TOKEN_KEY、SH_APP_TOKEN_KEY。tel_phone。新增/修改入口在业务层检查有效业务账号手机号唯一,软删除账号可复用;不增加数据库唯一索引。ruoyi-admin/src/main/resources/application.yml:三组 LINE Channel 与 App 回跳配置。ruoyi-admin/.../utils/oauth/OAuthVerifyService.java:按 provider 选择 LINE Channel。ruoyi-admin/.../user/LineCallbackController.java:共用回调按 provider 分流。ruoyi-admin/.../user/InfoUserController.java:分角色绑定、签发 token、手机号唯一校验。ruoyi-admin/.../stall/StallController.java:摊主新增统一写入 tel_phone 并检查业务手机号唯一。ruoyi-admin/.../user/BusinessPhoneService.java:有效业务账号手机号唯一检查。ruoyi-admin/.../user/MerchantSubaccountApplicationService.java:子账号改用 tel_phone。updatesql/sql.md:冲突预检后迁移 line provider 和历史业务账号手机号字段。specs/017-oauth-login/*:同步接口及前端参数说明。三组 Channel Secret 不写入仓库,部署环境分别提供 LINE_USER_CLIENT_SECRET、LINE_RIDER_CLIENT_SECRET、LINE_MERCHANT_CLIENT_SECRET;缺失时 LINE 登录配置校验直接拒绝请求。
ruoyi-admin 模块编译。state 的完整 CSRF 防护需服务端签发/消费并由三个 App 保存、回跳比对,作为三端协议联调项统一实施,不能只改单侧回调。