# 多方式登录与第三方登录设计文档 > **日期**: 2026-06-07 > **状态**: 已批准,待实现 > **作者**: AI Assistant --- ## 1. 背景与目标 ### 1.1 现状 当前系统仅支持用户名密码登录(`grant_type=password`),且 `PasswordAuthenticationConverter` 只提取 `username` 参数,无法支持手机号、邮箱登录。微信、支付宝、短信登录的 `Converter` 和 `Provider` 均为空实现。 ### 1.2 目标 1. **扩展密码登录**:支持用户名、手机号、邮箱三种账号类型登录 2. **实现短信登录**:框架结构先行,验证码逻辑后续填充 3. **实现微信登录**:支持微信授权码换取用户信息并自动创建账号 4. **实现支付宝登录**:支持支付宝授权码换取用户信息并自动创建账号 5. **第三方账号管理**:存储 openId/unionId,支持 unionId 优先查询 6. **手机号为主键**:系统以手机号作为用户唯一标识,第三方登录自动创建新用户 7. **字段迁移**:将 `email` 从 `uc_user_detail` 迁移到 `uc_user` 表 --- ## 2. 核心设计原则 1. **独立授权模式**:每种登录方式使用独立的 `grant_type`,符合 OAuth2 扩展规范 2. **手机号唯一性**:手机号是系统用户的唯一标识,第三方登录时优先用手机号创建/查找用户 3. **自动创建用户**:第三方登录无手机号时,自动生成 `userNo` 作为用户名,后续用户可自行修改 4. **unionId 优先**:查询第三方用户信息时,优先使用 unionId,其次使用 openId 5. **向后兼容**:保留现有 `password` 模式的 `username` 参数,同时新增 `account` + `accountType` 参数 --- ## 3. 架构设计 ### 3.1 整体架构 ``` 前端调用 │ ▼ POST /oauth2/token │ ▼ ┌─────────────────────────────────────┐ │ DelegatingAuthenticationConverter │ │ ┌─────────┐ ┌─────┐ ┌─────────┐ │ │ │ Password│ │ Sms │ │ Wechat │ │ │ │ Converter│ │Converter│ │Converter│ │ │ └─────────┘ └─────┘ └─────────┘ │ │ ┌─────────┐ │ │ │ Alipay │ │ │ │Converter│ │ │ └─────────┘ │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ AuthenticationProvider 链 │ │ ┌─────────┐ ┌─────┐ ┌─────────┐ │ │ │ Password│ │ Sms │ │ Wechat │ │ │ │ Provider│ │Provider│ │Provider│ │ │ └─────────┘ └─────┘ └─────────┘ │ │ ┌─────────┐ │ │ │ Alipay │ │ │ │Provider │ │ │ └─────────┘ │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ 用户查找 / 创建 / 绑定 │ │ • 根据手机号/用户名/邮箱查找用户 │ │ • 第三方登录:调平台API获取用户信息 │ │ • 自动创建新用户(手机号或userNo) │ │ • 记录第三方绑定关系 │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ 生成 OAuth2 Token │ │ Access Token + Refresh Token │ └─────────────────────────────────────┘ ``` ### 3.2 登录方式对照表 | 登录方式 | grant_type | 必填参数 | 可选参数 | 说明 | |---------|-----------|---------|---------|------| | 用户名密码 | `password` | `username`, `password` | - | 兼容现有方式 | | 手机号密码 | `password` | `account`, `accountType=PHONE`, `password` | - | 扩展方式 | | 邮箱密码 | `password` | `account`, `accountType=EMAIL`, `password` | - | 扩展方式 | | 短信验证码 | `sms` | `phone`, `code` | - | 框架先行 | | 微信登录 | `wechat` | `code` | `phone` | 授权码模式 | | 支付宝登录 | `alipay` | `code` | `phone` | 授权码模式 | --- ## 4. 数据库设计 ### 4.1 新增表:`rui_uc_user_social` 存储用户与第三方平台的绑定关系。 ```sql CREATE TABLE rui_uc_user_social ( id BIGINT NOT NULL, tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID', user_id BIGINT NOT NULL COMMENT '用户ID', provider VARCHAR(20) NOT NULL COMMENT '平台 wechat/alipay', union_id VARCHAR(100) DEFAULT NULL COMMENT 'unionId(微信开放平台)', open_id VARCHAR(100) NOT NULL COMMENT 'openId', extra JSON DEFAULT NULL COMMENT '扩展信息(昵称、头像等)', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id), UNIQUE KEY uk_user_provider (user_id, provider), UNIQUE KEY uk_provider_openid (provider, open_id), INDEX idx_union_id (union_id), INDEX idx_user_id (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户第三方账号关联'; ``` **字段说明**: - `provider`: 平台标识,`wechat` 或 `alipay` - `union_id`: 微信开放平台统一标识,同一主体下的不同应用 unionId 相同 - `open_id`: 各应用内的唯一标识 - `extra`: JSON 格式,存储第三方平台的额外信息(昵称、头像、性别等) **索引设计**: - `uk_user_provider`: 一个用户在同一平台只能绑定一个账号 - `uk_provider_openid`: 同一平台的 openId 唯一 - `idx_union_id`: 支持 unionId 查询 ### 4.2 修改表:`rui_uc_user` 新增 `email` 字段: ```sql -- 在 rui_uc_user 表中添加 email 字段 ALTER TABLE rui_uc_user ADD COLUMN email VARCHAR(100) DEFAULT NULL COMMENT '邮箱' AFTER phone; ALTER TABLE rui_uc_user ADD UNIQUE KEY uk_email (tenant_id, email); ``` 修改后的表结构: ```sql CREATE TABLE rui_uc_user ( id BIGINT NOT NULL, tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID 0:系统级', username VARCHAR(100) NOT NULL COMMENT '用户名', phone VARCHAR(20) DEFAULT NULL COMMENT '手机号', email VARCHAR(100) DEFAULT NULL COMMENT '邮箱', user_no VARCHAR(50) DEFAULT NULL COMMENT '用户编号(短编码,前端展示用)', password VARCHAR(255) NOT NULL COMMENT '密码(BCrypt加密)', user_type TINYINT NOT NULL DEFAULT 1 COMMENT '用户类型 1:普通用户 2:管理员 3:系统用户', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态 0:禁用 1:启用', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除 0:正常 1:已删', created_by BIGINT DEFAULT NULL COMMENT '创建者ID', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_by BIGINT DEFAULT NULL COMMENT '更新者ID', updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id), UNIQUE KEY uk_username (tenant_id, username), UNIQUE KEY uk_phone (tenant_id, phone), UNIQUE KEY uk_email (tenant_id, email), INDEX idx_tenant (tenant_id), INDEX idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户'; ``` ### 4.3 修改表:`rui_uc_user_detail` 删除 `email` 字段: ```sql -- 从 rui_uc_user_detail 表中删除 email 字段 ALTER TABLE rui_uc_user_detail DROP COLUMN email; ``` ### 4.4 登录日志扩展 `rui_sys_login_log` 表的 `login_type` 字段已有定义: - `1`: 密码登录 - `2`: 短信登录 - `3`: 微信登录 - `4`: 支付宝登录 **无需修改**,但需要在代码中确保所有登录方式都正确记录类型。 --- ## 5. 核心流程设计 ### 5.1 密码登录流程(扩展) ``` 前端请求 │ ▼ POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic {client_credentials} # 方式1:用户名密码(兼容现有) grant_type=password &username=admin &password=123456 # 方式2:手机号密码(新增) grant_type=password &account=13800138000 &accountType=PHONE &password=123456 # 方式3:邮箱密码(新增) grant_type=password &account=user@example.com &accountType=EMAIL &password=123456 │ ▼ PasswordAuthenticationConverter ├─ 提取 grant_type=password ├─ 如果有 username → 走兼容模式 └─ 如果有 account + accountType → 走扩展模式 │ ▼ PasswordAuthenticationProvider ├─ 校验客户端支持 password 授权 ├─ 构建 UsernamePasswordAuthenticationToken │ ├─ 兼容模式: username 作为 principal │ └─ 扩展模式: account 作为 principal │ ▼ AuthenticationManager │ ▼ DaoAuthenticationProvider ├─ 调用 RemoteUserDetailsService.loadUserByUsername(username) │ 或 RemoteUserDetailsService.loadUserByAccount(account, accountType) │ ▼ RemoteUserDetailsService ├─ USERNAME → userAuthFeign.loadUser(account) ├─ PHONE → userAuthFeign.loadUser({account, PHONE}) └─ EMAIL → userAuthFeign.loadUser({account, EMAIL}) │ ▼ UserInnerController.loadUser(LoginAccountDTO) ├─ 根据 accountType 查询用户 ├─ PHONE → lambdaQuery().eq(User::getPhone, account) ├─ EMAIL → lambdaQuery().eq(User::getEmail, account) └─ USERNAME → lambdaQuery().eq(User::getUsername, account) │ ▼ 返回 UserDetails → 生成 Token ``` ### 5.2 短信登录流程 ``` 前端请求 │ ▼ POST /oauth2/token grant_type=sms &phone=13800138000 &code=123456 │ ▼ SmsAuthenticationConverter ├─ 校验 grant_type=sms ├─ 校验 phone 必填 └─ 校验 code 必填 │ ▼ SmsAuthenticationProvider ├─ 校验客户端支持 sms 授权 ├─ 从 Redis 获取验证码(key: sms:code:{phone}) ├─ 比对验证码 ├─ 验证码错误 → 抛出异常 └─ 验证码正确 → 继续 │ ▼ 根据 phone 查询用户 ├─ 找到 → 生成 Token └─ 未找到 → 创建新用户 ├─ username = phone ├─ phone = phone ├─ password = 随机生成(BCrypt加密) └─ user_no = 自动生成 │ ▼ 生成 OAuth2 Token ``` **注意**:短信验证码发送接口(`POST /sms/send`)本次不实现,只预留框架结构。Redis 中的验证码需要前端开发时手动设置或通过其他方式注入。 ### 5.3 微信登录流程 ``` 前端请求 │ ▼ POST /oauth2/token grant_type=wechat &code=wx_auth_code &phone=13800138000 ← 可选 │ ▼ WechatAuthenticationConverter ├─ 校验 grant_type=wechat ├─ 校验 code 必填 └─ 提取 phone(可选) │ ▼ WechatAuthenticationProvider ├─ 校验客户端支持 wechat 授权 ├─ 调用微信 API 换取 access_token │ GET https://api.weixin.qq.com/sns/oauth2/access_token │ ?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code │ ├─ 获取 openId, unionId, access_token │ ├─ 根据 unionId 查询 rui_uc_user_social │ ├─ 找到 → 获取 user_id → 查询用户 → 生成 Token │ └─ 未找到 → 根据 openId 查询 │ ├─ 找到 → 获取 user_id → 查询用户 → 生成 Token │ └─ 未找到 → 创建新用户 │ ▼ 创建新用户流程 ├─ 有 phone 参数 │ ├─ 查询 phone 是否已存在 │ ├─ 存在 → 使用该用户,记录绑定关系 │ └─ 不存在 → 创建新用户 │ ├─ username = phone │ ├─ phone = phone │ └─ password = 随机生成 │ └─ 无 phone 参数 ├─ username = 随机生成(如 WX_ + 时间戳) ├─ phone = null └─ password = 随机生成 │ ▼ 记录绑定关系 INSERT INTO rui_uc_user_social (user_id, provider, union_id, open_id, extra) VALUES (?, 'wechat', ?, ?, ?) │ ▼ 生成 OAuth2 Token ``` ### 5.4 支付宝登录流程 与微信登录类似,区别: 1. 调用支付宝 API:`alipay.system.oauth.token` 换取 access_token 2. 调用 `alipay.user.info.share` 获取用户信息 3. 支付宝没有 unionId,使用 userId 作为唯一标识 4. 存储到 `rui_uc_user_social` 时,`union_id` 为 null --- ## 6. 代码结构 ### 6.1 新增/修改文件清单 #### rui-common-oauth2 模块 ``` rui-common-oauth2/src/main/java/com/rui/common/oauth2/ ├── authentication/ │ ├── BaseAuthenticationConverter.java # 已有,无需修改 │ ├── BaseAuthenticationProvider.java # 已有,无需修改 │ ├── password/ │ │ ├── PasswordAuthenticationConverter.java # 修改:支持 accountType │ │ └── PasswordAuthenticationProvider.java # 已有,无需修改 │ ├── sms/ │ │ ├── SmsAuthenticationConverter.java # 重写:实现短信参数提取 │ │ ├── SmsAuthenticationProvider.java # 重写:实现短信认证逻辑 │ │ └── SmsAuthenticationToken.java # 新增:短信认证令牌 │ ├── weixin/ │ │ ├── WeixinAuthenticationConverter.java # 重写:实现微信参数提取 │ │ ├── WeixinAuthenticationProvider.java # 重写:实现微信认证逻辑 │ │ └── WeixinAuthenticationToken.java # 新增:微信认证令牌 │ └── alipay/ │ ├── AlipayAuthenticationConverter.java # 重写:实现支付宝参数提取 │ ├── AlipayAuthenticationProvider.java # 重写:实现支付宝认证逻辑 │ └── AlipayAuthenticationToken.java # 新增:支付宝认证令牌 ├── config/ │ └── OAuth2ServerConfig.java # 修改:注册新的 Converter 和 Provider └── service/ └── RemoteUserDetailsService.java # 修改:支持 EMAIL 类型 ``` #### rui-service-user 模块 ``` rui-service-user/src/main/java/com/rui/service/user/ ├── entity/ │ ├── User.java # 修改:新增 email 字段 │ ├── UserDetail.java # 修改:删除 email 字段 │ └── UserSocial.java # 新增:第三方账号关联实体 ├── mapper/ │ └── UserSocialMapper.java # 新增 ├── service/ │ ├── IUserSocialService.java # 新增 │ └── impl/ │ └── UserSocialServiceImpl.java # 新增 ├── controller/ │ └── inner/ │ └── UserInnerController.java # 修改:支持 EMAIL 查询 └── dto/ └── LoginAccountDTO.java # 已有,无需修改 ``` ### 6.2 关键类设计 #### 6.2.1 PasswordAuthenticationConverter(修改) ```java public class PasswordAuthenticationConverter extends BaseAuthenticationConverter { @Override public void checkParams(HttpServletRequest request) { MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); // 兼容模式:使用 username String username = parameters.getFirst("username"); if (StringUtils.hasText(username)) { // 校验 password String password = parameters.getFirst("password"); if (!StringUtils.hasText(password)) { OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, "password", ...); } return; } // 扩展模式:使用 account + accountType String account = parameters.getFirst("account"); if (!StringUtils.hasText(account)) { OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, "account", ...); } String accountType = parameters.getFirst("accountType"); if (!StringUtils.hasText(accountType)) { OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, "accountType", ...); } // 校验 password String password = parameters.getFirst("password"); if (!StringUtils.hasText(password)) { OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, "password", ...); } } @Override public PasswordAuthenticationToken buildToken(...) { // 将 accountType 放入 additionalParameters // 供 Provider 使用 return new PasswordAuthenticationToken(...); } } ``` #### 6.2.2 WechatAuthenticationProvider(重写) ```java @Slf4j public class WechatAuthenticationProvider extends BaseAuthenticationProvider { private final WechatApiClient wechatApiClient; private final UserSocialService userSocialService; private final UserService userService; @Override public UsernamePasswordAuthenticationToken buildToken(Map reqParameters) { String code = (String) reqParameters.get("code"); String phone = (String) reqParameters.get("phone"); // 调用微信 API 获取 openId, unionId WechatTokenResponse wxResponse = wechatApiClient.getAccessToken(code); String openId = wxResponse.getOpenid(); String unionId = wxResponse.getUnionid(); // 查找或创建用户 User user = findOrCreateUser(openId, unionId, phone); // 构建认证令牌 return new UsernamePasswordAuthenticationToken(user.getUsername(), null); } private User findOrCreateUser(String openId, String unionId, String phone) { // 1. 根据 unionId 查找 if (StringUtils.hasText(unionId)) { UserSocial social = userSocialService.findByUnionId(unionId); if (social != null) { return userService.getById(social.getUserId()); } } // 2. 根据 openId 查找 UserSocial social = userSocialService.findByOpenId("wechat", openId); if (social != null) { return userService.getById(social.getUserId()); } // 3. 创建新用户 User user = new User(); if (StringUtils.hasText(phone)) { // 检查手机号是否已存在 User existUser = userService.findByPhone(phone); if (existUser != null) { user = existUser; } else { user.setUsername(phone); user.setPhone(phone); user.setPassword(generateRandomPassword()); userService.save(user); } } else { // 无手机号,生成随机用户名 user.setUsername(generateRandomUsername()); user.setPassword(generateRandomPassword()); userService.save(user); } // 4. 记录绑定关系 UserSocial newSocial = new UserSocial(); newSocial.setUserId(user.getId()); newSocial.setProvider("wechat"); newSocial.setUnionId(unionId); newSocial.setOpenId(openId); userSocialService.save(newSocial); return user; } } ``` #### 6.2.3 UserSocial 实体 ```java @Data @TableName(value = "uc_user_social", keepGlobalPrefix = true) public class UserSocial extends BaseEntity { @Schema(description = "用户ID") private Long userId; @Schema(description = "平台 wechat/alipay") private String provider; @Schema(description = "unionId") private String unionId; @Schema(description = "openId") private String openId; @Schema(description = "扩展信息") private String extra; } ``` --- ## 7. API 接口设计 ### 7.1 密码登录 ```http ### 用户名密码登录(兼容现有) POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic c3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Q6 grant_type=password &username=admin &password=123456 &scope=server ### 手机号密码登录(新增) POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic c3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Q6 grant_type=password &account=13800138000 &accountType=PHONE &password=123456 &scope=server ### 邮箱密码登录(新增) POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic c3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Q6 grant_type=password &account=user@example.com &accountType=EMAIL &password=123456 &scope=server ``` ### 7.2 短信登录 ```http ### 短信验证码登录 POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic c3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Q6 grant_type=sms &phone=13800138000 &code=123456 &scope=server ``` ### 7.3 微信登录 ```http ### 微信登录 POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic c3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Q6 grant_type=wechat &code=wx_auth_code_xxx &phone=13800138000 &scope=server ``` ### 7.4 支付宝登录 ```http ### 支付宝登录 POST /oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic c3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Qtc3ByaW5nLWJvb3Q6 grant_type=alipay &code=alipay_auth_code_xxx &phone=13800138000 &scope=server ``` ### 7.5 响应格式 所有登录方式返回统一的 OAuth2 Token 响应: ```json { "access_token": "abc123...", "token_type": "Bearer", "expires_in": 7200, "refresh_token": "def456...", "scope": "server" } ``` --- ## 8. 配置设计 ### 8.1 微信配置 ```yaml # Nacos 配置:rui-service-auth.yaml 或 rui-common.yaml social: wechat: app-id: wx1234567890abcdef app-secret: your-app-secret # 可选:token 刷新地址 token-url: https://api.weixin.qq.com/sns/oauth2/access_token # 可选:用户信息地址 user-info-url: https://api.weixin.qq.com/sns/userinfo ``` ### 8.2 支付宝配置 ```yaml social: alipay: app-id: 2024XXXXXXXXXXXX private-key: your-private-key public-key: alipay-public-key # 可选:网关地址 gateway-url: https://openapi.alipay.com/gateway.do ``` ### 8.3 客户端授权类型配置 修改 `sys_oauth_client` 表,为客户端添加新的授权类型: ```sql -- 更新默认客户端,支持所有登录方式 UPDATE sys_oauth_client SET grant_types = 'password,refresh_token,client_credentials,sms,wechat,alipay' WHERE client_id = 'rui-client'; ``` --- ## 9. 安全设计 ### 9.1 验证码安全 - 短信验证码有效期:5 分钟 - 验证码错误次数限制:5 次/小时 - 验证码存储:Redis,key = `sms:code:{phone}` ### 9.2 第三方登录安全 - 微信/支付宝授权码只能使用一次 - 授权码有效期:5 分钟(由微信/支付宝平台控制) - 后端必须校验授权码的真实性(调平台 API) ### 9.3 密码安全 - 第三方登录自动创建的用户,生成随机密码(32 位随机字符串) - 用户首次设置密码时,要求提供原密码或通过手机验证码验证 --- ## 10. 错误码设计 | 错误码 | 描述 | 场景 | |-------|------|------| | `invalid_request` | 请求参数错误 | 缺少必填参数、参数格式错误 | | `invalid_grant` | 授权失败 | 验证码错误、授权码无效 | | `invalid_client` | 客户端认证失败 | 客户端不存在、授权类型不支持 | | `unauthorized_client` | 客户端未授权 | 客户端不支持该授权类型 | | `server_error` | 服务器内部错误 | 调用第三方 API 失败 | --- ## 11. 测试策略 ### 11.1 单元测试 - `PasswordAuthenticationConverterTest`: 测试参数提取和校验 - `SmsAuthenticationProviderTest`: 测试验证码校验逻辑 - `WechatAuthenticationProviderTest`: Mock 微信 API,测试用户创建流程 ### 11.2 集成测试 - 使用 H2 内存数据库测试完整登录流程 - 使用 WireMock 模拟微信/支付宝 API ### 11.3 手动测试清单 - [ ] 用户名密码登录(兼容测试) - [ ] 手机号密码登录 - [ ] 邮箱密码登录 - [ ] 短信验证码登录(使用 Redis 手动设置验证码) - [ ] 微信登录(使用测试授权码) - [ ] 支付宝登录(使用测试授权码) - [ ] 第三方登录后绑定手机号 - [ ] 同一微信不同手机号创建不同用户 - [ ] unionId 优先查询验证 --- ## 12. 风险与回滚 ### 12.1 风险 | 风险 | 影响 | 缓解措施 | |------|------|---------| | 微信/支付宝 API 变更 | 登录失败 | 封装 API 调用,便于快速适配 | | 手机号重复 | 数据不一致 | 数据库唯一索引 + 代码校验 | | 性能问题 | 登录慢 | Redis 缓存 + 异步记录日志 | ### 12.2 回滚方案 - 数据库变更:保留原字段,新增字段不影响现有数据 - 代码回滚:新授权模式独立实现,不影响现有 `password` 模式 - 配置回滚:移除新 grant_type 即可禁用 --- ## 13. 后续优化 1. **短信服务商接入**:实现真实的短信发送功能 2. **社交账号解绑**:提供 API 解除第三方绑定 3. **多账号合并**:支持将多个第三方账号合并到同一用户 4. **登录设备管理**:记录登录设备,支持远程登出 5. **扫码登录**:支持微信扫码登录 PC 端 --- ## 14. 附录 ### 14.1 登录类型枚举 ```java public enum LoginType { PASSWORD(1, "密码登录"), SMS(2, "短信登录"), WECHAT(3, "微信登录"), ALIPAY(4, "支付宝登录"); private final int code; private final String description; LoginType(int code, String description) { this.code = code; this.description = description; } } ``` ### 14.2 账号类型枚举 ```java public enum AccountType { USERNAME("用户名"), PHONE("手机号"), EMAIL("邮箱"); private final String description; AccountType(String description) { this.description = description; } } ``` ### 14.3 第三方平台枚举 ```java public enum SocialProvider { WECHAT("微信"), ALIPAY("支付宝"); private final String description; SocialProvider(String description) { this.description = description; } } ``` --- **文档结束**