登录接口完善
This commit is contained in:
16
src/main/java/com/bicloud/common/enums/LoginType.java
Normal file
16
src/main/java/com/bicloud/common/enums/LoginType.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package com.bicloud.common.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录类型枚举
|
||||||
|
*/
|
||||||
|
public enum LoginType {
|
||||||
|
/**
|
||||||
|
* 密码登录
|
||||||
|
*/
|
||||||
|
PASSWORD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信验证码登录
|
||||||
|
*/
|
||||||
|
SMS_CODE
|
||||||
|
}
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
//package com.bicloud.common.exception;
|
|
||||||
//
|
|
||||||
//import com.bicloud.common.result.Result;
|
|
||||||
//import lombok.extern.slf4j.Slf4j;
|
|
||||||
//import org.springframework.validation.BindException;
|
|
||||||
//import org.springframework.validation.FieldError;
|
|
||||||
//import org.springframework.web.bind.MethodArgumentNotValidException;
|
|
||||||
//import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
//import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * 全局异常处理器
|
|
||||||
// * 统一处理各类异常,返回友好的错误提示
|
|
||||||
// */
|
|
||||||
//@Slf4j
|
|
||||||
//@RestControllerAdvice(basePackages = "com.bicloud.controller")
|
|
||||||
//public class GlobalExceptionHandler {
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 处理业务异常
|
|
||||||
// */
|
|
||||||
// @ExceptionHandler(BusinessException.class)
|
|
||||||
// public Result<?> handleBusinessException(BusinessException e) {
|
|
||||||
// log.warn("业务异常:{}", e.getMessage());
|
|
||||||
// return Result.error(e.getMessage());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 处理 @Valid 参数校验异常(用于 @RequestBody)
|
|
||||||
// * 当 RegisterDto 等 DTO 的字段验证失败时触发
|
|
||||||
// */
|
|
||||||
// @ExceptionHandler(MethodArgumentNotValidException.class)
|
|
||||||
// public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
|
||||||
// // 获取第一个错误信息
|
|
||||||
// String errorMessage = e.getBindingResult().getFieldErrors()
|
|
||||||
// .stream()
|
|
||||||
// .map(FieldError::getDefaultMessage)
|
|
||||||
// .findFirst()
|
|
||||||
// .orElse("参数校验失败");
|
|
||||||
//
|
|
||||||
// log.warn("参数校验失败:{}", errorMessage);
|
|
||||||
// return Result.error(errorMessage);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 处理 @Validated 参数校验异常(用于 @RequestParam, @PathVariable)
|
|
||||||
// */
|
|
||||||
// @ExceptionHandler(BindException.class)
|
|
||||||
// public Result<?> handleBindException(BindException e) {
|
|
||||||
// String errorMessage = e.getFieldErrors()
|
|
||||||
// .stream()
|
|
||||||
// .map(FieldError::getDefaultMessage)
|
|
||||||
// .findFirst()
|
|
||||||
// .orElse("参数校验失败");
|
|
||||||
//
|
|
||||||
// log.warn("参数绑定失败:{}", errorMessage);
|
|
||||||
// return Result.error(errorMessage);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 处理其他未捕获的异常
|
|
||||||
// */
|
|
||||||
// @ExceptionHandler(Exception.class)
|
|
||||||
// public Result<?> handleException(Exception e) {
|
|
||||||
// log.error("系统异常", e);
|
|
||||||
// return Result.error("系统异常,请联系管理员");
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -27,10 +27,19 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
|||||||
"/v3/api-docs/**",
|
"/v3/api-docs/**",
|
||||||
"/webjars/**",
|
"/webjars/**",
|
||||||
"/favicon.ico",
|
"/favicon.ico",
|
||||||
|
// 静态资源路径放行(新增)
|
||||||
|
"/img/**",
|
||||||
|
"/video/**",
|
||||||
// 用户相关放行路径
|
// 用户相关放行路径
|
||||||
"/user/code",
|
"/user/code",
|
||||||
"/user/register",
|
"/user/register",
|
||||||
"/user/login"
|
"/user/login",
|
||||||
|
"/course/cats",
|
||||||
|
"/course/page",
|
||||||
|
"/document/page",
|
||||||
|
"/document/types"
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ public class UserController {
|
|||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
@Operation(summary = "用户登录")
|
@Operation(summary = "用户登录")
|
||||||
public Result<LoginVo> login(@Valid @RequestBody LoginDto loginDto) {
|
public Result<LoginVo> login(@Valid @RequestBody LoginDto loginDto) {
|
||||||
LoginVo loginVo = userService.login(loginDto);
|
return userService.login(loginDto);
|
||||||
return Result.success(loginVo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.bicloud.pojo.dto;
|
package com.bicloud.pojo.dto;
|
||||||
|
|
||||||
|
import com.bicloud.common.enums.LoginType;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +17,13 @@ public class LoginDto {
|
|||||||
@NotBlank(message = "登录标识不能为空")
|
@NotBlank(message = "登录标识不能为空")
|
||||||
private String identifier;
|
private String identifier;
|
||||||
|
|
||||||
@Schema(description = "密码", example = "Password123")
|
@Schema(description = "密码(密码登录时必填)", example = "Password123")
|
||||||
@NotBlank(message = "密码不能为空")
|
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@Schema(description = "验证码(验证码登录时必填)", example = "123123")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "登录类型:PASSWORD-密码登录,SMS_CODE-短信验证码登录", example = "PASSWORD")
|
||||||
|
@NotNull(message = "登录类型不能为空")
|
||||||
|
private LoginType loginType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public interface UserService {
|
|||||||
/**
|
/**
|
||||||
* 用户登录
|
* 用户登录
|
||||||
*/
|
*/
|
||||||
LoginVo login(LoginDto loginDto);
|
Result<LoginVo> login(LoginDto loginDto);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户登出
|
* 用户登出
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.bicloud.service.impl;
|
package com.bicloud.service.impl;
|
||||||
|
|
||||||
import com.bicloud.common.context.UserContext;
|
import com.bicloud.common.context.UserContext;
|
||||||
|
import com.bicloud.common.enums.LoginType;
|
||||||
import com.bicloud.common.exception.BusinessException;
|
import com.bicloud.common.exception.BusinessException;
|
||||||
import com.bicloud.common.result.Result;
|
import com.bicloud.common.result.Result;
|
||||||
import com.bicloud.common.utils.JwtUtils;
|
import com.bicloud.common.utils.JwtUtils;
|
||||||
@@ -20,6 +21,7 @@ import jakarta.servlet.http.HttpSession;
|
|||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -161,11 +163,57 @@ public class UserServiceImpl implements UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoginVo login(LoginDto loginDto) {
|
public Result<LoginVo> login(LoginDto loginDto) {
|
||||||
|
String identifier = loginDto.getIdentifier();
|
||||||
|
LoginType loginType = loginDto.getLoginType();
|
||||||
|
|
||||||
|
// 根据登录类型执行不同的登录逻辑
|
||||||
|
Result<User> userResult = null;
|
||||||
|
|
||||||
|
if (loginType == LoginType.PASSWORD) {
|
||||||
|
// 密码登录
|
||||||
|
userResult = loginByPassword(loginDto);
|
||||||
|
} else if (loginType == LoginType.SMS_CODE) {
|
||||||
|
// 验证码登录
|
||||||
|
userResult = loginBySmsCode(loginDto);
|
||||||
|
} else {
|
||||||
|
return Result.error("不支持的登录类型!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查登录结果(code != 200 表示失败)
|
||||||
|
if (userResult.getCode() != 200) {
|
||||||
|
return Result.error(userResult.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userResult.getData();
|
||||||
|
|
||||||
|
// 生成Token
|
||||||
|
String token = jwtUtils.generateToken(user.getId(), user.getUsername());
|
||||||
|
|
||||||
|
// 构建返回结果
|
||||||
|
LoginVo loginVo = LoginVo.builder()
|
||||||
|
.token(token)
|
||||||
|
.expiresIn(jwtProperties.getTokenExpiration())
|
||||||
|
.userId(user.getId())
|
||||||
|
.username(user.getUsername())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return Result.success(loginVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码登录
|
||||||
|
*/
|
||||||
|
private Result<User> loginByPassword(LoginDto loginDto) {
|
||||||
String identifier = loginDto.getIdentifier();
|
String identifier = loginDto.getIdentifier();
|
||||||
String password = loginDto.getPassword();
|
String password = loginDto.getPassword();
|
||||||
|
|
||||||
// 1. 根据登录标识类型查询用户
|
// 1. 校验密码不能为空
|
||||||
|
if (!StringUtils.hasText(password)) {
|
||||||
|
return Result.error("密码不能为空!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 根据登录标识类型查询用户
|
||||||
User user = null;
|
User user = null;
|
||||||
if (RegexUtils.isPhoneValid(identifier)) {
|
if (RegexUtils.isPhoneValid(identifier)) {
|
||||||
// 手机号登录
|
// 手机号登录
|
||||||
@@ -178,26 +226,77 @@ public class UserServiceImpl implements UserService {
|
|||||||
user = userMapper.selectByUsername(identifier);
|
user = userMapper.selectByUsername(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 验证用户是否存在
|
// 3. 验证用户是否存在
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new BusinessException("用户不存在!");
|
return Result.error("用户不存在!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 验证密码
|
// 4. 验证密码
|
||||||
if (!PasswordUtils.verifyPassword(password, user.getSalt(), user.getPwd())) {
|
if (!PasswordUtils.verifyPassword(password, user.getSalt(), user.getPwd())) {
|
||||||
throw new BusinessException("密码错误!");
|
return Result.error("密码错误!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 生成Token
|
return Result.success(user);
|
||||||
String token = jwtUtils.generateToken(user.getId(), user.getUsername());
|
}
|
||||||
|
|
||||||
// 5. 构建返回结果
|
/**
|
||||||
return LoginVo.builder()
|
* 短信验证码登录
|
||||||
.token(token)
|
*/
|
||||||
.expiresIn(jwtProperties.getTokenExpiration())
|
private Result<User> loginBySmsCode(LoginDto loginDto) {
|
||||||
.userId(user.getId())
|
String identifier = loginDto.getIdentifier();
|
||||||
.username(user.getUsername())
|
String code = loginDto.getCode();
|
||||||
.build();
|
|
||||||
|
// 1. 校验验证码不能为空
|
||||||
|
if (!StringUtils.hasText(code)) {
|
||||||
|
return Result.error("验证码不能为空!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 验证码登录只支持手机号
|
||||||
|
if (!RegexUtils.isPhoneValid(identifier)) {
|
||||||
|
return Result.error("验证码登录仅支持手机号!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 从Redis获取验证码
|
||||||
|
String cacheCode = null;
|
||||||
|
try {
|
||||||
|
cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + identifier);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("从Redis获取验证码失败,手机号:{},错误信息:{}", identifier, e.getMessage());
|
||||||
|
return Result.error("验证码获取失败,请稍后重试!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 验证码不存在或已过期
|
||||||
|
if (cacheCode == null) {
|
||||||
|
return Result.error("验证码已过期,请重新获取!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 验证验证码是否正确
|
||||||
|
if (!cacheCode.equals(code)) {
|
||||||
|
return Result.error("验证码错误!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 根据手机号查询用户
|
||||||
|
User user = null;
|
||||||
|
try {
|
||||||
|
user = userMapper.selectByMobile(identifier);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("查询用户失败,手机号:{},错误信息:{}", identifier, e.getMessage());
|
||||||
|
return Result.error("查询用户失败,请稍后重试!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
return Result.error("该手机号未注册!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 验证码验证成功后删除Redis中的验证码(防止重复使用)
|
||||||
|
try {
|
||||||
|
stringRedisTemplate.delete(LOGIN_CODE_KEY + identifier);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除Redis验证码失败,手机号:{},错误信息:{}", identifier, e.getMessage());
|
||||||
|
// 删除失败不影响登录,只记录日志
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user