forked from admin/innovation-platform
feat: 添加用户登录功能
- 创建 SysUserMapper 数据访问层 - 创建 SysUserService/SysUserServiceImpl 业务逻辑层 - 创建 AuthController 认证控制器 - 实现登录、注册、登出接口 - 添加全局异常处理器 - 配置 Sa-Token 跨域支持 - 使用 BCrypt 密码加密
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
package com.innovation.platform.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token 配置类
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跨域配置
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedOriginPatterns("*")
|
||||||
|
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||||
|
.allowedHeaders("*")
|
||||||
|
.allowCredentials(true)
|
||||||
|
.maxAge(3600);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.innovation.platform.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
|
import com.innovation.platform.common.Result;
|
||||||
|
import com.innovation.platform.dto.LoginRequest;
|
||||||
|
import com.innovation.platform.dto.LoginResponse;
|
||||||
|
import com.innovation.platform.dto.RegisterRequest;
|
||||||
|
import com.innovation.platform.service.SysUserService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证控制器
|
||||||
|
*/
|
||||||
|
@Tag(name = "认证管理", description = "登录、注册、登出接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/auth")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
private final SysUserService sysUserService;
|
||||||
|
|
||||||
|
@SaIgnore
|
||||||
|
@Operation(summary = "用户登录")
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Result<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||||
|
LoginResponse response = sysUserService.login(request);
|
||||||
|
return Result.success("登录成功", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SaIgnore
|
||||||
|
@Operation(summary = "用户注册")
|
||||||
|
@PostMapping("/register")
|
||||||
|
public Result<Void> register(@Valid @RequestBody RegisterRequest request) {
|
||||||
|
sysUserService.register(request);
|
||||||
|
return Result.success("注册成功", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "用户登出")
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public Result<Void> logout() {
|
||||||
|
sysUserService.logout();
|
||||||
|
return Result.success("登出成功", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.innovation.platform.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录请求 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LoginRequest {
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
private String password;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.innovation.platform.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录响应 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoginResponse {
|
||||||
|
private String token;
|
||||||
|
private Long userId;
|
||||||
|
private String username;
|
||||||
|
private String realName;
|
||||||
|
private String avatar;
|
||||||
|
private Integer roleType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.innovation.platform.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册请求 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RegisterRequest {
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank(message = "真实姓名不能为空")
|
||||||
|
private String realName;
|
||||||
|
|
||||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private Integer gender;
|
||||||
|
|
||||||
|
private Integer roleType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.innovation.platform.exception;
|
||||||
|
|
||||||
|
import com.innovation.platform.common.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;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(RuntimeException.class)
|
||||||
|
public Result<Void> handleRuntimeException(RuntimeException e) {
|
||||||
|
log.error("业务异常: {}", e.getMessage(), e);
|
||||||
|
return Result.error(400, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数校验异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public Result<Void> handleValidationException(MethodArgumentNotValidException e) {
|
||||||
|
String message = e.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(FieldError::getDefaultMessage)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
log.error("参数校验失败: {}", message);
|
||||||
|
return Result.error(400, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
public Result<Void> handleBindException(BindException e) {
|
||||||
|
String message = e.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(FieldError::getDefaultMessage)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
log.error("参数绑定失败: {}", message);
|
||||||
|
return Result.error(400, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public Result<Void> handleException(Exception e) {
|
||||||
|
log.error("系统异常: {}", e.getMessage(), e);
|
||||||
|
return Result.error(500, "系统异常,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.innovation.platform.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.innovation.platform.entity.SysUser;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户 Mapper 接口
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.innovation.platform.service;
|
||||||
|
|
||||||
|
import com.innovation.platform.dto.LoginRequest;
|
||||||
|
import com.innovation.platform.dto.LoginResponse;
|
||||||
|
import com.innovation.platform.dto.RegisterRequest;
|
||||||
|
import com.innovation.platform.entity.SysUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务接口
|
||||||
|
*/
|
||||||
|
public interface SysUserService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
*/
|
||||||
|
LoginResponse login(LoginRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*/
|
||||||
|
void register(RegisterRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登出
|
||||||
|
*/
|
||||||
|
void logout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名查询用户
|
||||||
|
*/
|
||||||
|
SysUser getByUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询用户
|
||||||
|
*/
|
||||||
|
SysUser getById(Long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.innovation.platform.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.crypto.digest.BCrypt;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.innovation.platform.dto.LoginRequest;
|
||||||
|
import com.innovation.platform.dto.LoginResponse;
|
||||||
|
import com.innovation.platform.dto.RegisterRequest;
|
||||||
|
import com.innovation.platform.entity.SysUser;
|
||||||
|
import com.innovation.platform.mapper.SysUserMapper;
|
||||||
|
import com.innovation.platform.service.SysUserService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SysUserServiceImpl implements SysUserService {
|
||||||
|
|
||||||
|
private final SysUserMapper sysUserMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginResponse login(LoginRequest request) {
|
||||||
|
// 查询用户
|
||||||
|
SysUser user = getByUsername(request.getUsername());
|
||||||
|
if (user == null) {
|
||||||
|
throw new RuntimeException("用户名或密码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
if (!BCrypt.checkpw(request.getPassword(), user.getPassword())) {
|
||||||
|
throw new RuntimeException("用户名或密码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户状态
|
||||||
|
if (user.getStatus() != null && user.getStatus() == 0) {
|
||||||
|
throw new RuntimeException("账号已被禁用");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
StpUtil.login(user.getId());
|
||||||
|
|
||||||
|
// 返回登录信息
|
||||||
|
return LoginResponse.builder()
|
||||||
|
.token(StpUtil.getTokenValue())
|
||||||
|
.userId(user.getId())
|
||||||
|
.username(user.getUsername())
|
||||||
|
.realName(user.getRealName())
|
||||||
|
.avatar(user.getAvatar())
|
||||||
|
.roleType(user.getRoleType())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void register(RegisterRequest request) {
|
||||||
|
// 检查用户名是否已存在
|
||||||
|
if (getByUsername(request.getUsername()) != null) {
|
||||||
|
throw new RuntimeException("用户名已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建用户
|
||||||
|
SysUser user = new SysUser();
|
||||||
|
user.setUsername(request.getUsername());
|
||||||
|
user.setPassword(BCrypt.hashpw(request.getPassword()));
|
||||||
|
user.setRealName(request.getRealName());
|
||||||
|
user.setPhone(request.getPhone());
|
||||||
|
user.setEmail(request.getEmail());
|
||||||
|
user.setGender(request.getGender());
|
||||||
|
user.setRoleType(request.getRoleType() != null ? request.getRoleType() : 0);
|
||||||
|
user.setStatus(1);
|
||||||
|
|
||||||
|
sysUserMapper.insert(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout() {
|
||||||
|
StpUtil.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SysUser getByUsername(String username) {
|
||||||
|
return sysUserMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<SysUser>()
|
||||||
|
.eq(SysUser::getUsername, username)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SysUser getById(Long id) {
|
||||||
|
return sysUserMapper.selectById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user