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

10 KiB
Raw Blame History

登录接口优化总结

🎯 优化目标

简化用户登录流程,使用手机号+短信验证码登录,若用户不存在则自动注册。

完成的优化

1. 简化LoginRequest

文件: backend-single/src/main/java/com/emotion/dto/request/LoginRequest.java

修改前:需要账号、密码、图形验证码、验证码key等字段

修改后:仅需2个字段

@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 新的登录流程

@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 自动注册用户

/**
 * 自动注册用户(用于登录时用户不存在的情况)
 * 
 * @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 生成随机密码

/**
 * 生成随机密码
 * 格式: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. 优化短信验证码发送

修改前:检查手机号是否已注册,已注册则抛出异常

修改后:根据是否已注册返回不同的提示信息

@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

/**
 * 用户登录(简化版:手机号+验证码,不存在则自动注册)
 */
@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:获取短信验证码

请求:

curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13900139000"

响应:

{
  "code": 200,
  "message": "验证码已发送",
  "data": {
    "code": "123456",
    "expiresIn": 300,
    "message": "验证码已发送,用于注册验证,有效期5分钟"
  }
}

步骤2:登录(自动注册)

请求:

curl -X POST "http://localhost:19089/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "13900139000",
    "smsCode": "123456"
  }'

响应:

{
  "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:获取短信验证码

请求:

curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13900139000"

响应:

{
  "code": 200,
  "message": "验证码已发送",
  "data": {
    "code": "123456",
    "expiresIn": 300,
    "message": "验证码已发送,用于登录验证,有效期5分钟"
  }
}

步骤2:登录

请求:

curl -X POST "http://localhost:19089/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "13900139000",
    "smsCode": "123456"
  }'

响应:

{
  "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() 方法

当前代码:

// TODO: 接入真实的短信服务商(阿里云、腾讯云等)
// 目前使用固定验证码 123456
String code = DEFAULT_SMS_CODE;

后续改进:

// 生成随机6位数字验证码
String code = String.format("%06d", new Random().nextInt(1000000));

// 调用短信服务商API发送验证码
// 示例:阿里云短信服务
// smsService.sendSms(phone, code);

2. 生产环境优化

需要修改的地方:

  1. 移除响应中的验证码

    return SmsCodeResponse.builder()
            // .code(code)  // 生产环境删除此行
            .expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60)
            .message(message)
            .build();
    
  2. 添加发送频率限制

    • 同一手机号60秒内只能发送一次
    • 同一IP每天最多发送10次
  3. 添加验证码尝试次数限制

    • 同一手机号最多尝试5次
    • 超过次数后需要重新获取验证码

验证清单

  • 编译成功,无错误
  • LoginRequest简化为2个字段
  • 实现自动注册逻辑
  • 实现随机密码生成
  • 优化短信验证码提示信息
  • 更新Controller注解
  • 测试新用户登录(自动注册)
  • 测试已注册用户登录
  • 添加详细的日志记录

🎉 总结

成功优化了登录接口,实现了以下目标:

  1. 简化登录流程 - 仅需手机号和短信验证码
  2. 自动注册机制 - 用户不存在时自动注册
  3. 智能提示信息 - 根据用户状态返回不同提示
  4. 无缝用户体验 - 无需区分注册和登录
  5. 良好的代码质量 - 清晰的注释、完善的日志、标准的异常处理

现在用户可以通过简单的2步完成登录或注册:

  1. 输入手机号获取验证码
  2. 输入验证码完成登录(不存在则自动注册)

登录成功后自动返回访问令牌和用户信息,用户体验大大提升!