Files
rui-docs/superpowers/specs/2026-06-07-multi-login-social-login-design.md
T

853 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 多方式登录与第三方登录设计文档
> **日期**: 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<PasswordAuthenticationToken> {
@Override
public void checkParams(HttpServletRequest request) {
MultiValueMap<String, String> 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<WechatAuthenticationToken> {
private final WechatApiClient wechatApiClient;
private final UserSocialService userSocialService;
private final UserService userService;
@Override
public UsernamePasswordAuthenticationToken buildToken(Map<String, Object> 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 次/小时
- 验证码存储:Rediskey = `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;
}
}
```
---
**文档结束**