plan.md 7.5 KB

017 实施计划

架构:最大化复用现有登录链路

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) → 返回
  • token 签发:复用 JwtUtil.setToken(CacheConstants.USER_TOKEN_KEY, loginDto),仅给 LoginUserDtoprovider 字段并在 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='三方账号绑定';

API 契约

1) 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 } }

2) POST /infouser/user/oauthBindPhone (@Anonymous)

请求 OAuthBindDto

tempKey, phone, code   // code=短信验证码
cid, cidType, deviceToken, voIPToken

响应:{ code:200, msg, data: InfoUser, token }

三家凭证校验(OAuthVerifyService)

provider 凭证 校验 取 providerUid 依赖
apple identityToken(ES256 JWT) appleid.apple.com/auth/keys(JWKS) 验签 + 校 iss/aud/exp sub nimbus-jose-jwt(新增)
google 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.javaOAuthBindDto.java
改 controller ruoyi-admin/.../user/InfoUserController.java(+2 方法)
改 token ruoyi-common/.../model/LoginUserDto.javaruoyi-system/.../utils/JwtUtil.java
改配置 ruoyi-admin/src/main/resources/application.yml(oauth 段)
改依赖 ruoyi-admin/pom.xml(+nimbus-jose-jwt)
SQL updatesql/sql.md

风险 / 待验证

  • Apple / Google / LINE 的凭证校验需用各家真实 token + 正确 clientId/bundleId 联调才能端到端验证;配置值(clientId/jwks url)由前端/运营提供后填入 application.yml。
  • Apple JWKS 建议加缓存(key 偶尔轮换);初版每次拉取或加内存缓存。

2026-09-04 增量:用户/骑手/商家 LINE 登录

最小改造方案

  • 保留前端自行构造 LINE 授权 URL 的现有方式,不新增后端授权入口。
  • 共用 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_riderline_merchant 使用 tel_phone 且只绑定已有账号。
  • 登录成功后分别使用 USER_TOKEN_KEYQS_TOKEN_KEYSH_APP_TOKEN_KEY
  • 商家子账号除启用状态外,还必须能解析到有效归属商家/门店权限,失效归属不得通过 LINE 登录。
  • 骑手、商家及商家子账号统一使用 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_SECRETLINE_RIDER_CLIENT_SECRETLINE_MERCHANT_CLIENT_SECRET;缺失时 LINE 登录配置校验直接拒绝请求。

验证

  • 单元测试覆盖 provider 配置选择、角色与 token key 映射、手机号重复判断。
  • JDK 21 定向测试与 ruoyi-admin 模块编译。
  • 真实 LINE Channel code、回调地址和三个 App scheme 仍需客户端联调验证。
  • state 的完整 CSRF 防护需服务端签发/消费并由三个 App 保存、回跳比对,作为三端协议联调项统一实施,不能只改单侧回调。