接口定义:UserAuthController.register()
@PostMapping("/register")
public ApiResult<UserRegisterResponse> register(@RequestBody @Validated UserRegisterRequest request) {
return userAuthService.register(request);
}
功能职责:
@Validated
注解)请求参数(UserRegisterRequest):
{
"username": "enterprise_user1",
"password": "Password@123",
"confirmPassword": "Password@123",
"mobile": "13812345678",
"email": "test@example.com",
"userType": "ENTERPRISE",
"enterpriseInfo": {
"enterpriseName": "测试企业有限公司",
"creditCode": "91110000ABCDEFG123",
"legalPerson": "张三",
"contactPhone": "13812345678"
}
}
响应结果(UserRegisterResponse):
{
"code": 200,
"message": "注册成功",
"data": {
"userId": "1234567890",
"username": "enterprise_user1",
"userType": "ENTERPRISE",
"registrationTime": "2023-08-15T10:30:45Z"
}
}
接口定义:UserAuthController.login()
@PostMapping("/login")
public ApiResult<LoginResponse> login(@RequestBody @Validated LoginRequest request) {
return userAuthService.login(request);
}
功能职责:
请求参数(LoginRequest):
{
"username": "enterprise_user1",
"password": "Password@123"
}
响应结果(LoginResponse):
{
"code": 200,
"message": "登录成功",
"data": {
"userId": "1234567890",
"username": "enterprise_user1",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600
}
}
接口定义:UserAuthController.refreshToken()
@PostMapping("/refresh-token")
public ApiResult<RefreshTokenResponse> refreshToken(@RequestBody @Validated RefreshTokenRequest request) {
return userAuthService.refreshToken(request);
}
接口定义:UserAuthController.logout()
@PostMapping("/logout")
public ApiResult<Void> logout(@RequestHeader("Authorization") String token) {
return userAuthService.logout(token);
}
主要实现类:UserAuthServiceImpl
register()
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<UserRegisterResponse> register(UserRegisterRequest request) {
// 1. 检查用户名是否已存在
if (userMapper.existsByUsername(request.getUsername())) {
return ApiResult.failed("用户名已存在");
}
// 2. 检查手机号是否已注册
if (userMapper.existsByMobile(request.getMobile())) {
return ApiResult.failed("手机号已注册");
}
// 3. 密码加密
String encryptedPassword = passwordEncoder.encode(request.getPassword());
// 4. 构建用户对象
User user = new User();
user.setUserId(IdGenerator.generateId());
user.setUsername(request.getUsername());
user.setPassword(encryptedPassword);
user.setMobile(request.getMobile());
user.setEmail(request.getEmail());
user.setUserType(request.getUserType());
user.setStatus(UserStatus.NORMAL);
user.setCreateTime(new Date());
// 5. 保存用户信息
userMapper.insert(user);
// 6. 企业用户需保存企业信息
if (UserType.ENTERPRISE.equals(request.getUserType())) {
EnterpriseInfo enterpriseInfo = request.getEnterpriseInfo();
enterpriseInfo.setUserId(user.getUserId());
enterpriseInfo.setVerifyStatus(VerifyStatus.UNVERIFIED);
enterpriseInfoMapper.insert(enterpriseInfo);
}
// 7. 分配默认角色权限
roleService.assignDefaultRole(user.getUserId(), user.getUserType());
// 8. 发送注册成功通知
notificationService.sendRegistrationNotification(user);
// 9. 构建返回结果
UserRegisterResponse response = new UserRegisterResponse();
response.setUserId(user.getUserId());
response.setUsername(user.getUsername());
response.setUserType(user.getUserType());
response.setRegistrationTime(user.getCreateTime());
return ApiResult.success("注册成功", response);
}
处理逻辑:
login()
@Override
public ApiResult<LoginResponse> login(LoginRequest request) {
// 1. 根据用户名查询用户
User user = userMapper.selectByUsername(request.getUsername());
if (user == null) {
return ApiResult.failed("用户名或密码错误");
}
// 2. 检查用户状态
if (!UserStatus.NORMAL.equals(user.getStatus())) {
return ApiResult.failed("账号已被禁用,请联系管理员");
}
// 3. 验证密码
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
// 4. 记录登录失败次数
loginFailService.recordFailedLogin(user.getUserId());
return ApiResult.failed("用户名或密码错误");
}
// 5. 清除登录失败记录
loginFailService.clearFailedLogin(user.getUserId());
// 6. 生成JWT令牌
String token = jwtTokenProvider.generateToken(user);
String refreshToken = jwtTokenProvider.generateRefreshToken(user);
// 7. 保存令牌信息
tokenService.saveToken(user.getUserId(), token, refreshToken);
// 8. 记录登录日志
auditLogService.logLogin(user.getUserId(), request.getLoginIp(), LoginResult.SUCCESS);
// 9. 构建返回结果
LoginResponse response = new LoginResponse();
response.setUserId(user.getUserId());
response.setUsername(user.getUsername());
response.setToken(token);
response.setRefreshToken(refreshToken);
response.setExpiresIn(jwtTokenProvider.getExpirationTime());
return ApiResult.success("登录成功", response);
}
处理逻辑:
主要实现类:JwtTokenProviderImpl
generateToken()
@Override
public String generateToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + tokenExpirationMs);
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getUserId());
claims.put("username", user.getUsername());
claims.put("userType", user.getUserType());
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS256, jwtSecret)
.compact();
}
validateToken()
@Override
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
log.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
log.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
log.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
log.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
log.error("JWT claims string is empty");
}
return false;
}
主要实现类:UserMapper
@Mapper
public interface UserMapper {
/**
* 插入用户
*/
int insert(User user);
/**
* 根据用户名查询
*/
User selectByUsername(String username);
/**
* 检查用户名是否存在
*/
boolean existsByUsername(String username);
/**
* 检查手机号是否存在
*/
boolean existsByMobile(String mobile);
/**
* 根据ID查询
*/
User selectById(String userId);
/**
* 更新用户信息
*/
int update(User user);
}
主要实现类:EnterpriseInfoMapper
@Mapper
public interface EnterpriseInfoMapper {
/**
* 插入企业信息
*/
int insert(EnterpriseInfo enterpriseInfo);
/**
* 根据用户ID查询
*/
EnterpriseInfo selectByUserId(String userId);
/**
* 根据统一社会信用代码查询
*/
EnterpriseInfo selectByCreditCode(String creditCode);
/**
* 更新企业信息
*/
int update(EnterpriseInfo enterpriseInfo);
}
主要实现类:JwtAuthenticationFilter
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
// 1. 从请求中获取JWT令牌
String jwt = getJwtFromRequest(request);
// 2. 验证令牌有效性
if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
// 3. 从令牌中获取用户ID
String userId = jwtTokenProvider.getUserIdFromToken(jwt);
// 4. 加载用户详情
UserDetails userDetails = userDetailsService.loadUserById(userId);
// 5. 创建认证对象
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 6. 设置认证上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
// 7. 继续过滤器链
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
处理逻辑:
@Data
@Table(name = "t_user")
public class User implements Serializable {
@Id
private String userId;
private String username;
private String password;
private String mobile;
private String email;
@Enumerated(EnumType.STRING)
private UserType userType;
@Enumerated(EnumType.STRING)
private UserStatus status;
private Date createTime;
private Date updateTime;
}
@Data
@Table(name = "t_enterprise_info")
public class EnterpriseInfo implements Serializable {
@Id
private String id;
private String userId;
private String enterpriseName;
private String creditCode;
private String legalPerson;
private String contactPhone;
@Enumerated(EnumType.STRING)
private VerifyStatus verifyStatus;
private Date createTime;
private Date updateTime;
}
主要实现类:GlobalExceptionHandler
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理参数验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
StringBuilder sb = new StringBuilder("参数校验失败:");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
}
String msg = sb.toString();
log.warn(msg);
return ApiResult.validateFailed(msg);
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public ApiResult<String> handleBusinessException(BusinessException ex) {
log.error("业务异常", ex);
return ApiResult.failed(ex.getCode(), ex.getMessage());
}
/**
* 处理未授权异常
*/
@ExceptionHandler(AccessDeniedException.class)
public ApiResult<String> handleAccessDeniedException(AccessDeniedException ex) {
log.error("权限不足", ex);
return ApiResult.forbidden("没有权限执行此操作");
}
/**
* 处理其他未知异常
*/
@ExceptionHandler(Exception.class)
public ApiResult<String> handleException(Exception ex) {
log.error("系统异常", ex);
return ApiResult.failed("服务器内部错误,请稍后重试");
}
}
主要实现类:TokenServiceImpl
@Service
@RequiredArgsConstructor
public class TokenServiceImpl implements TokenService {
private final RedisTemplate<String, Object> redisTemplate;
private final JwtTokenProvider jwtTokenProvider;
private static final String TOKEN_KEY_PREFIX = "auth:token:";
private static final String REFRESH_TOKEN_KEY_PREFIX = "auth:refresh_token:";
@Override
public void saveToken(String userId, String token, String refreshToken) {
// 保存访问令牌,过期时间与JWT令牌一致
String tokenKey = TOKEN_KEY_PREFIX + userId;
redisTemplate.opsForValue().set(tokenKey, token, jwtTokenProvider.getExpirationTime(), TimeUnit.MILLISECONDS);
// 保存刷新令牌,过期时间更长
String refreshTokenKey = REFRESH_TOKEN_KEY_PREFIX + userId;
redisTemplate.opsForValue().set(refreshTokenKey, refreshToken, jwtTokenProvider.getRefreshExpirationTime(), TimeUnit.MILLISECONDS);
}
@Override
public String getToken(String userId) {
String key = TOKEN_KEY_PREFIX + userId;
Object token = redisTemplate.opsForValue().get(key);
return token != null ? token.toString() : null;
}
@Override
public String getRefreshToken(String userId) {
String key = REFRESH_TOKEN_KEY_PREFIX + userId;
Object refreshToken = redisTemplate.opsForValue().get(key);
return refreshToken != null ? refreshToken.toString() : null;
}
@Override
public void removeToken(String userId) {
String tokenKey = TOKEN_KEY_PREFIX + userId;
String refreshTokenKey = REFRESH_TOKEN_KEY_PREFIX + userId;
redisTemplate.delete(tokenKey);
redisTemplate.delete(refreshTokenKey);
}
}
主要实现类:AuditLogServiceImpl
@Service
@RequiredArgsConstructor
public class AuditLogServiceImpl implements AuditLogService {
private final AuditLogMapper auditLogMapper;
@Override
public void logLogin(String userId, String ip, LoginResult result) {
AuditLog log = new AuditLog();
log.setUserId(userId);
log.setOperationType(OperationType.LOGIN);
log.setOperationResult(result.toString());
log.setIp(ip);
log.setCreateTime(new Date());
auditLogMapper.insert(log);
}
@Override
public void logLogout(String userId, String ip) {
AuditLog log = new AuditLog();
log.setUserId(userId);
log.setOperationType(OperationType.LOGOUT);
log.setOperationResult(LoginResult.SUCCESS.toString());
log.setIp(ip);
log.setCreateTime(new Date());
auditLogMapper.insert(log);
}
}
┌──────────┐ ┌───────────────┐ ┌─────────────┐ ┌──────────┐ ┌─────────┐
│ 客户端 │ │ 控制器层 │ │ 服务层 │ │ 数据访问层│ │ 缓存层 │
└────┬─────┘ └───────┬───────┘ └──────┬──────┘ └────┬─────┘ └────┬────┘
│ │ │ │ │
│ 1. 登录请求 │ │ │ │
│ ───────────────────────> │ │ │
│ │ │ │ │
│ │ 2. 调用登录服务 │ │ │
│ │ ─────────────────────────> │ │
│ │ │ │ │
│ │ │ 3. 查询用户 │ │
│ │ │ ─────────────────────> │
│ │ │ │ │
│ │ │ 4. 返回用户数据 │ │
│ │ │ <───────────────────── │
│ │ │ │ │
│ │ │ 5. 生成令牌 │ │
│ │ │ ─────────────────────────────────────────> │
│ │ │ │ │
│ │ │ 6. 令牌缓存完成 │ │
│ │ │ <───────────────────────────────────────── │
│ │ │ │ │
│ │ 7. 返回登录结果 │ │ │
│ │ <───────────────────────── │ │
│ │ │ │ │
│ 8. 返回令牌 │ │ │ │
│ <─────────────────────── │ │ │
│ │ │ │ │
│ 9. 后续请求(带令牌) │ │ │ │
│ ───────────────────────> │ │ │
│ │ │ │ │
│ │ 10.令牌校验 │ │ │
│ │ ─────────────────────────> │ │
│ │ │ │ │
│ │ 11.校验通过 │ │ │
│ │ <───────────────────────── │ │
│ │ │ │ │
│ 12.返回请求结果 │ │ │ │
│ <─────────────────────── │ │ │
│ │ │ │ │