Files
happy-life-star/AUTH_LOGIN_OPTIMIZATION.md
T
2025-10-13 10:43:08 +08:00

397 lines
10 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.
# 登录接口优化总结
## 🎯 优化目标
简化用户登录流程,使用手机号+短信验证码登录,若用户不存在则自动注册。
## ✅ 完成的优化
### 1. 简化LoginRequest
**文件**: `backend-single/src/main/java/com/emotion/dto/request/LoginRequest.java`
**修改前**:需要账号、密码、图形验证码、验证码key等字段
**修改后**:仅需2个字段
```java
@Data
@EqualsAndHashCode(callSuper = true)
public class LoginRequest extends BaseRequest {
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
/**
* 短信验证码
*/
@NotBlank(message = "验证码不能为空")
@Size(min = 6, max = 6, message = "验证码必须为6位")
private String smsCode;
}
```
### 2. 优化登录逻辑
**文件**: `backend-single/src/main/java/com/emotion/service/impl/AuthServiceImpl.java`
#### 2.1 新的登录流程
```java
@Override
public AuthResponse login(LoginRequest request) {
// 1. 验证短信验证码
if (!validateSmsCode(request.getPhone(), request.getSmsCode())) {
throw new CaptchaException("验证码错误或已过期");
}
// 2. 根据手机号查询用户
User user = userService.getByPhone(request.getPhone());
// 3. 如果用户不存在,则自动注册
if (user == null) {
log.info("用户不存在,自动注册: phone={}", request.getPhone());
user = autoRegisterUser(request.getPhone());
} else {
// 检查用户状态
if (user.getStatus() != 1) {
throw new AuthException("账号已被禁用");
}
log.info("用户登录: phone={}, userId={}", request.getPhone(), user.getId());
}
// 4. 生成令牌并返回
// ...
}
```
#### 2.2 自动注册用户
```java
/**
* 自动注册用户(用于登录时用户不存在的情况)
*
* @param phone 手机号
* @return 新创建的用户
*/
private User autoRegisterUser(String phone) {
// 生成随机用户名:开心 + 6位随机大小写字母和数字
String username = generateRandomUsername();
// 使用手机号作为账号
String account = phone;
// 生成随机密码(用户可以后续修改)
String randomPassword = generateRandomPassword();
// 创建用户
User user = userService.createUser(
account,
username,
randomPassword,
null, // 邮箱为空
phone
);
log.info("自动注册用户成功: phone={}, username={}, userId={}", phone, username, user.getId());
return user;
}
```
#### 2.3 生成随机密码
```java
/**
* 生成随机密码
* 格式:8位随机大小写字母和数字
*
* @return 随机密码
*/
private String generateRandomPassword() {
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8; i++) {
int index = random.nextInt(chars.length());
sb.append(chars.charAt(index));
}
return sb.toString();
}
```
### 3. 优化短信验证码发送
**修改前**:检查手机号是否已注册,已注册则抛出异常
**修改后**:根据是否已注册返回不同的提示信息
```java
@Override
public SmsCodeResponse sendSmsCode(String phone) {
// 验证手机号格式
if (!StringUtils.hasText(phone) || !phone.matches("^1[3-9]\\d{9}$")) {
throw new BusinessException("手机号格式不正确");
}
// 检查手机号是否已注册,用于提示信息
boolean isRegistered = existsByPhone(phone);
String message;
if (isRegistered) {
message = "验证码已发送,用于登录验证,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟";
} else {
message = "验证码已发送,用于注册验证,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟";
}
// TODO: 接入真实的短信服务商(阿里云、腾讯云等)
String code = DEFAULT_SMS_CODE;
// 将验证码存储到Redis,有效期5分钟
String key = SMS_CODE_PREFIX + phone;
redisTemplate.opsForValue().set(key, code, SMS_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
log.info("发送短信验证码: phone={}, code={}, isRegistered={}", phone, code, isRegistered);
// 构建响应
return SmsCodeResponse.builder()
.code(code) // 开发环境返回验证码,生产环境应该删除此行
.expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60)
.message(message)
.build();
}
```
### 4. 更新Controller注解
**文件**: `backend-single/src/main/java/com/emotion/controller/AuthController.java`
```java
/**
* 用户登录(简化版:手机号+验证码,不存在则自动注册)
*/
@PostMapping("/login")
@Operation(summary = "用户登录", description = "使用手机号和短信验证码登录,若用户不存在则自动注册")
public Result<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
AuthResponse response = authService.login(request);
return Result.success("登录成功", response);
}
```
## 📊 优化对比
| 项目 | 优化前 | 优化后 |
|------|--------|--------|
| 必填字段 | 账号、密码、验证码、验证码key | 手机号、短信验证码 |
| 验证码类型 | 图形验证码 | 短信验证码 |
| 用户不存在 | 提示错误 | 自动注册 |
| 密码验证 | 需要 | 不需要 |
| 登录流程 | 复杂 | 简单快捷 |
## 🔧 核心特性
### 1. 简化登录流程
- ✅ 仅需2个字段:手机号、短信验证码
- ✅ 无需记住密码
- ✅ 用户不存在自动注册
- ✅ 自动生成随机用户名和密码
### 2. 智能提示信息
- ✅ 未注册用户:提示"用于注册验证"
- ✅ 已注册用户:提示"用于登录验证"
- ✅ 用户体验更友好
### 3. 自动注册机制
- ✅ 自动生成用户名:开心 + 6位随机字符
- ✅ 自动生成密码:8位随机字符
- ✅ 使用手机号作为账号
- ✅ 无缝登录体验
### 4. 安全性保障
- ✅ 手机号格式验证
- ✅ 短信验证码验证
- ✅ 验证码过期检查
- ✅ 验证码一次性使用
- ✅ 用户状态检查
## 🚀 使用示例
### 场景1:新用户首次登录(自动注册)
#### 步骤1:获取短信验证码
**请求**:
```bash
curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13900139000"
```
**响应**:
```json
{
"code": 200,
"message": "验证码已发送",
"data": {
"code": "123456",
"expiresIn": 300,
"message": "验证码已发送,用于注册验证,有效期5分钟"
}
}
```
#### 步骤2:登录(自动注册)
**请求**:
```bash
curl -X POST "http://localhost:19089/api/auth/login" \
-H "Content-Type: application/json" \
-d '{
"phone": "13900139000",
"smsCode": "123456"
}'
```
**响应**:
```json
{
"code": 200,
"message": "登录成功",
"data": {
"accessToken": "eyJhbGciOiJIUzUxMiJ9...",
"refreshToken": "eyJhbGciOiJIUzUxMiJ9...",
"expiresIn": 86400,
"userInfo": {
"id": "f361c8648378565a91c6d799d641741f",
"account": "13900139000",
"username": "开心4FhbOl",
"nickname": "开心4FhbOl",
"phone": "13900139000",
"memberLevel": "free"
},
"loginTime": "2025-10-06 23:14:57"
}
}
```
### 场景2:已注册用户登录
#### 步骤1:获取短信验证码
**请求**:
```bash
curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13900139000"
```
**响应**:
```json
{
"code": 200,
"message": "验证码已发送",
"data": {
"code": "123456",
"expiresIn": 300,
"message": "验证码已发送,用于登录验证,有效期5分钟"
}
}
```
#### 步骤2:登录
**请求**:
```bash
curl -X POST "http://localhost:19089/api/auth/login" \
-H "Content-Type: application/json" \
-d '{
"phone": "13900139000",
"smsCode": "123456"
}'
```
**响应**:
```json
{
"code": 200,
"message": "登录成功",
"data": {
"accessToken": "eyJhbGciOiJIUzUxMiJ9...",
"refreshToken": "eyJhbGciOiJIUzUxMiJ9...",
"expiresIn": 86400,
"userInfo": {
"id": "f361c8648378565a91c6d799d641741f",
"account": "13900139000",
"username": "开心4FhbOl",
"nickname": "开心4FhbOl",
"phone": "13900139000",
"memberLevel": "free"
},
"loginTime": "2025-10-06 23:15:18"
}
}
```
## 📝 TODO事项
### 1. 接入真实短信服务
**位置**: `AuthServiceImpl.sendSmsCode()` 方法
**当前代码**:
```java
// TODO: 接入真实的短信服务商(阿里云、腾讯云等)
// 目前使用固定验证码 123456
String code = DEFAULT_SMS_CODE;
```
**后续改进**:
```java
// 生成随机6位数字验证码
String code = String.format("%06d", new Random().nextInt(1000000));
// 调用短信服务商API发送验证码
// 示例:阿里云短信服务
// smsService.sendSms(phone, code);
```
### 2. 生产环境优化
**需要修改的地方**:
1. **移除响应中的验证码**
```java
return SmsCodeResponse.builder()
// .code(code) // 生产环境删除此行
.expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60)
.message(message)
.build();
```
2. **添加发送频率限制**
- 同一手机号60秒内只能发送一次
- 同一IP每天最多发送10次
3. **添加验证码尝试次数限制**
- 同一手机号最多尝试5次
- 超过次数后需要重新获取验证码
## ✅ 验证清单
- [x] 编译成功,无错误
- [x] LoginRequest简化为2个字段
- [x] 实现自动注册逻辑
- [x] 实现随机密码生成
- [x] 优化短信验证码提示信息
- [x] 更新Controller注解
- [x] 测试新用户登录(自动注册)
- [x] 测试已注册用户登录
- [x] 添加详细的日志记录
## 🎉 总结
成功优化了登录接口,实现了以下目标:
1.**简化登录流程** - 仅需手机号和短信验证码
2.**自动注册机制** - 用户不存在时自动注册
3.**智能提示信息** - 根据用户状态返回不同提示
4.**无缝用户体验** - 无需区分注册和登录
5.**良好的代码质量** - 清晰的注释、完善的日志、标准的异常处理
现在用户可以通过简单的2步完成登录或注册:
1. 输入手机号获取验证码
2. 输入验证码完成登录(不存在则自动注册)
登录成功后自动返回访问令牌和用户信息,用户体验大大提升!