登录接口

This commit is contained in:
dww
2026-02-03 11:57:17 +08:00
parent 86fb565d4b
commit caa699a6cd
18 changed files with 749 additions and 57 deletions

18
pom.xml
View File

@@ -68,6 +68,24 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-test</artifactId>

View File

@@ -0,0 +1,85 @@
package com.bicloud.Interceptor;
import com.bicloud.common.context.UserContext;
import com.bicloud.common.utils.JwtUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import static com.bicloud.common.utils.RedisConstants.JWT_BLACKLIST_KEY;
/**
* JWT认证拦截器
*/
@Slf4j
@Component
public class JwtAuthInterceptor implements HandlerInterceptor {
@Resource
private JwtUtils jwtUtils;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 从请求头获取Token
String token = jwtUtils.extractToken(request);
// 2. 验证Token是否存在
if (!StringUtils.hasText(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"未登录,请先登录!\"}");
return false;
}
// 3. 验证Token有效性
if (!jwtUtils.validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"Token无效或已过期\"}");
return false;
}
// 4. 检查Token是否在黑名单中
String blacklistKey = JWT_BLACKLIST_KEY + token;
Boolean isBlacklisted = stringRedisTemplate.hasKey(blacklistKey);
if (Boolean.TRUE.equals(isBlacklisted)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"Token已失效请重新登录\"}");
return false;
}
// 5. 解析Token获取用户信息
Long userId = jwtUtils.getUserIdFromToken(token);
String username = jwtUtils.getUsernameFromToken(token);
if (userId == null || username == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"Token解析失败\"}");
return false;
}
// 6. 将用户信息存入ThreadLocal
UserContext.setUserId(userId);
UserContext.setUsername(username);
log.debug("用户认证成功userId: {}, username: {}", userId, username);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清除ThreadLocal防止内存泄漏
UserContext.clear();
}
}

View File

@@ -1,50 +0,0 @@
package com.bicloud.Interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.time.LocalDateTime;
/*
拦截器,请求打印请求状态
*/
@Component
public class RequestLogInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(RequestLogInterceptor.class);
private static final String START_TIME = "REQ_START_TIME";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
request.setAttribute(START_TIME, System.currentTimeMillis());
log.info("[REQ] time={}, method={}, uri={}, ip={}",
LocalDateTime.now(),
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
Long start = (Long) request.getAttribute(START_TIME);
long cost = (start == null) ? -1 : (System.currentTimeMillis() - start);
if (ex == null) {
log.info("[RES] status={}, costMs={}, uri={}",
response.getStatus(), cost, request.getRequestURI());
} else {
log.warn("[RES] status={}, costMs={}, uri={}, ex={}",
response.getStatus(), cost, request.getRequestURI(), ex.toString());
}
}
}

View File

@@ -0,0 +1,46 @@
package com.bicloud.common.context;
/**
* 用户上下文 - 使用ThreadLocal存储当前请求的用户信息
*/
public class UserContext {
private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
private static final ThreadLocal<String> USERNAME = new ThreadLocal<>();
/**
* 设置用户ID
*/
public static void setUserId(Long userId) {
USER_ID.set(userId);
}
/**
* 获取用户ID
*/
public static Long getUserId() {
return USER_ID.get();
}
/**
* 设置用户名
*/
public static void setUsername(String username) {
USERNAME.set(username);
}
/**
* 获取用户名
*/
public static String getUsername() {
return USERNAME.get();
}
/**
* 清除用户信息
*/
public static void clear() {
USER_ID.remove();
USERNAME.remove();
}
}

View File

@@ -0,0 +1,15 @@
package com.bicloud.common.exception;
/**
* 业务异常类
*/
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,68 @@
//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("系统异常,请联系管理员");
// }
//}

View File

@@ -0,0 +1,150 @@
package com.bicloud.common.utils;
import com.bicloud.config.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* JWT工具类
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtils {
private final JwtProperties jwtProperties;
private SecretKey secretKey;
@PostConstruct
public void init() {
// 初始化密钥
this.secretKey = Keys.hmacShaKeyFor(
jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8)
);
}
/**
* 生成AccessToken
*/
public String generateAccessToken(Long userId, String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtProperties.getAccessTokenExpiration() * 1000);
return Jwts.builder()
.subject(String.valueOf(userId))
.claim("username", username)
.issuedAt(now)
.expiration(expiryDate)
.signWith(secretKey)
.compact();
}
/**
* 生成RefreshToken
*/
public String generateRefreshToken(Long userId, String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtProperties.getRefreshTokenExpiration() * 1000);
return Jwts.builder()
.subject(String.valueOf(userId))
.claim("username", username)
.issuedAt(now)
.expiration(expiryDate)
.signWith(secretKey)
.compact();
}
/**
* 解析Token
*/
public Claims parseToken(String token) {
try {
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload();
} catch (Exception e) {
log.error("解析Token失败: {}", e.getMessage());
return null;
}
}
/**
* 从Token中获取用户ID
*/
public Long getUserIdFromToken(String token) {
Claims claims = parseToken(token);
if (claims == null) {
return null;
}
return Long.parseLong(claims.getSubject());
}
/**
* 从Token中获取用户名
*/
public String getUsernameFromToken(String token) {
Claims claims = parseToken(token);
if (claims == null) {
return null;
}
return claims.get("username", String.class);
}
/**
* 验证Token是否有效
*/
public boolean validateToken(String token) {
try {
Claims claims = parseToken(token);
if (claims == null) {
return false;
}
// 检查是否过期
Date expiration = claims.getExpiration();
return expiration.after(new Date());
} catch (Exception e) {
log.error("验证Token失败: {}", e.getMessage());
return false;
}
}
/**
* 从请求头中提取Token
*/
public String extractToken(HttpServletRequest request) {
String bearerToken = request.getHeader(jwtProperties.getTokenHeader());
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(jwtProperties.getTokenPrefix())) {
return bearerToken.substring(jwtProperties.getTokenPrefix().length());
}
return null;
}
/**
* 获取Token剩余有效时间
*/
public Long getTokenRemainingTime(String token) {
Claims claims = parseToken(token);
if (claims == null) {
return 0L;
}
Date expiration = claims.getExpiration();
long remainingTime = (expiration.getTime() - System.currentTimeMillis()) / 1000;
return Math.max(remainingTime, 0L);
}
}

View File

@@ -26,4 +26,8 @@ public class RedisConstants {
public static final String FEED_KEY = "feed:";
public static final String SHOP_GEO_KEY = "shop:geo:";
public static final String USER_SIGN_KEY = "sign:";
// JWT相关常量
public static final String JWT_BLACKLIST_KEY = "jwt:blacklist:";
public static final Long JWT_BLACKLIST_TTL = 7L; // 7天与RefreshToken过期时间一致
}

View File

@@ -29,6 +29,24 @@ public class RegexUtils {
return mismatch(code, RegexPatterns.VERIFY_CODE_REGEX);
}
/**
* 是否是有效手机格式
* @param phone 要校验的手机号
* @return true:有效false无效
*/
public static boolean isPhoneValid(String phone) {
return !isPhoneInvalid(phone);
}
/**
* 是否是有效邮箱格式
* @param email 要校验的邮箱
* @return true:有效false无效
*/
public static boolean isEmailValid(String email) {
return !isEmailInvalid(email);
}
// 校验是否不符合正则格式
private static boolean mismatch(String str, String regex){
if (StrUtil.isBlank(str)) {

View File

@@ -0,0 +1,39 @@
package com.bicloud.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* JWT配置属性类
*/
@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
/**
* JWT密钥
*/
private String secret;
/**
* AccessToken过期时间
*/
private Long accessTokenExpiration;
/**
* RefreshToken过期时间
*/
private Long refreshTokenExpiration;
/**
* Token请求头名称
*/
private String tokenHeader;
/**
* Token前缀
*/
private String tokenPrefix;
}

View File

@@ -1,6 +1,6 @@
package com.bicloud.config;
import com.bicloud.Interceptor.RequestLogInterceptor;
import com.bicloud.Interceptor.JwtAuthInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -8,24 +8,30 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final RequestLogInterceptor requestLogInterceptor;
private final JwtAuthInterceptor jwtAuthInterceptor;
public WebMvcConfig(RequestLogInterceptor requestLogInterceptor) {
this.requestLogInterceptor = requestLogInterceptor;
public WebMvcConfig(JwtAuthInterceptor jwtAuthInterceptor) {
this.jwtAuthInterceptor = jwtAuthInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestLogInterceptor)
// JWT认证拦截器
registry.addInterceptor(jwtAuthInterceptor)
.addPathPatterns("/**")
// 排除 Swagger/Knife4j 文档路径
.excludePathPatterns(
// Swagger/Knife4j 文档路径
"/doc.html",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/api-docs/**",
"/webjars/**",
"/favicon.ico"
"/favicon.ico",
// 用户相关放行路径
"/user/code",
"/user/register",
"/user/login",
"/user/refresh-token"
);
}
}

View File

@@ -2,7 +2,11 @@ package com.bicloud.controller;
import com.bicloud.common.result.Result;
import com.bicloud.common.utils.JwtUtils;
import com.bicloud.pojo.dto.LoginDto;
import com.bicloud.pojo.dto.RegisterDto;
import com.bicloud.pojo.vo.LoginVo;
import com.bicloud.pojo.vo.UserInfoVo;
import com.bicloud.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -22,6 +26,9 @@ public class UserController {
@Autowired
private UserService userService;
@Autowired
private JwtUtils jwtUtils;
/**
* 发送手机验证码
*/
@@ -41,4 +48,45 @@ public class UserController {
return userService.register(registerDto);
}
/**
* 用户登录
*/
@PostMapping("/login")
@Operation(summary = "用户登录")
public Result<LoginVo> login(@Valid @RequestBody LoginDto loginDto) {
LoginVo loginVo = userService.login(loginDto);
return Result.success(loginVo);
}
/**
* 刷新Token
*/
@PostMapping("/refresh-token")
@Operation(summary = "刷新Token")
public Result<String> refreshToken(@RequestParam("refreshToken") String refreshToken) {
String newAccessToken = userService.refreshToken(refreshToken);
return Result.success(newAccessToken);
}
/**
* 用户登出
*/
@PostMapping("/logout")
@Operation(summary = "用户登出")
public Result logout(HttpServletRequest request) {
String token = jwtUtils.extractToken(request);
userService.logout(token);
return Result.success("登出成功!");
}
/**
* 获取当前用户信息
*/
@GetMapping("/info")
@Operation(summary = "获取当前用户信息")
public Result<UserInfoVo> getUserInfo() {
UserInfoVo userInfo = userService.getCurrentUserInfo();
return Result.success(userInfo);
}
}

View File

@@ -0,0 +1,21 @@
package com.bicloud.pojo.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* 登录请求DTO
*/
@Data
@Schema(description = "登录请求")
public class LoginDto {
@Schema(description = "登录标识(手机号/邮箱/用户名)", example = "13800138000")
@NotBlank(message = "登录标识不能为空")
private String identifier;
@Schema(description = "密码", example = "Password123")
@NotBlank(message = "密码不能为空")
private String password;
}

View File

@@ -0,0 +1,36 @@
package com.bicloud.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 登录响应VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "登录响应")
public class LoginVo {
@Schema(description = "访问令牌")
private String accessToken;
@Schema(description = "刷新令牌")
private String refreshToken;
@Schema(description = "令牌类型", example = "Bearer")
private String tokenType;
@Schema(description = "访问令牌过期时间(秒)", example = "7200")
private Long expiresIn;
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "用户名")
private String username;
}

View File

@@ -0,0 +1,35 @@
package com.bicloud.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 用户信息VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "用户信息")
public class UserInfoVo {
@Schema(description = "用户ID")
private Long id;
@Schema(description = "用户名")
private String username;
@Schema(description = "手机号")
private String phone;
@Schema(description = "邮箱")
private String email;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -1,7 +1,10 @@
package com.bicloud.service;
import com.bicloud.common.result.Result;
import com.bicloud.pojo.dto.LoginDto;
import com.bicloud.pojo.dto.RegisterDto;
import com.bicloud.pojo.vo.LoginVo;
import com.bicloud.pojo.vo.UserInfoVo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@@ -9,4 +12,24 @@ public interface UserService {
Result sendCode(String mobile, HttpSession session, HttpServletRequest request);
Result register(RegisterDto registerDto);
/**
* 用户登录
*/
LoginVo login(LoginDto loginDto);
/**
* 刷新Token
*/
String refreshToken(String refreshToken);
/**
* 用户登出
*/
void logout(String token);
/**
* 获取当前用户信息
*/
UserInfoVo getCurrentUserInfo();
}

View File

@@ -1,11 +1,18 @@
package com.bicloud.service.impl;
import com.bicloud.common.context.UserContext;
import com.bicloud.common.exception.BusinessException;
import com.bicloud.common.result.Result;
import com.bicloud.common.utils.JwtUtils;
import com.bicloud.common.utils.PasswordUtils;
import com.bicloud.common.utils.RegexUtils;
import com.bicloud.config.JwtProperties;
import com.bicloud.mapper.UserMapper;
import com.bicloud.pojo.dto.LoginDto;
import com.bicloud.pojo.dto.RegisterDto;
import com.bicloud.pojo.entity.User;
import com.bicloud.pojo.vo.LoginVo;
import com.bicloud.pojo.vo.UserInfoVo;
import com.bicloud.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
@@ -28,6 +35,12 @@ public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private JwtUtils jwtUtils;
@Resource
private JwtProperties jwtProperties;
@Override
public Result sendCode(String mobile, HttpSession session, HttpServletRequest request) {
// 1.校验手机号
@@ -147,5 +160,115 @@ public class UserServiceImpl implements UserService {
return Result.success("注册成功!");
}
@Override
public LoginVo login(LoginDto loginDto) {
String identifier = loginDto.getIdentifier();
String password = loginDto.getPassword();
// 1. 根据登录标识类型查询用户
User user = null;
if (RegexUtils.isPhoneValid(identifier)) {
// 手机号登录
user = userMapper.selectByMobile(identifier);
} else if (RegexUtils.isEmailValid(identifier)) {
// 邮箱登录
user = userMapper.selectByEmail(identifier);
} else {
// 用户名登录
user = userMapper.selectByUsername(identifier);
}
// 2. 验证用户是否存在
if (user == null) {
throw new BusinessException("用户不存在!");
}
// 3. 验证密码
if (!PasswordUtils.verifyPassword(password, user.getSalt(), user.getPwd())) {
throw new BusinessException("密码错误!");
}
// 4. 生成Token
String accessToken = jwtUtils.generateAccessToken(user.getId(), user.getUsername());
String refreshToken = jwtUtils.generateRefreshToken(user.getId(), user.getUsername());
// 5. 构建返回结果
return LoginVo.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.tokenType("Bearer")
.expiresIn(jwtProperties.getAccessTokenExpiration())
.userId(user.getId())
.username(user.getUsername())
.build();
}
@Override
public String refreshToken(String refreshToken) {
// 1. 验证RefreshToken
if (!jwtUtils.validateToken(refreshToken)) {
throw new BusinessException("RefreshToken无效或已过期");
}
// 2. 检查黑名单
String blacklistKey = JWT_BLACKLIST_KEY + refreshToken;
Boolean isBlacklisted = stringRedisTemplate.hasKey(blacklistKey);
if (Boolean.TRUE.equals(isBlacklisted)) {
throw new BusinessException("Token已失效请重新登录");
}
// 3. 从Token中获取用户信息
Long userId = jwtUtils.getUserIdFromToken(refreshToken);
String username = jwtUtils.getUsernameFromToken(refreshToken);
if (userId == null || username == null) {
throw new BusinessException("Token解析失败");
}
// 4. 生成新的AccessToken
return jwtUtils.generateAccessToken(userId, username);
}
@Override
public void logout(String token) {
// 1. 将Token加入黑名单
String blacklistKey = JWT_BLACKLIST_KEY + token;
Long remainingTime = jwtUtils.getTokenRemainingTime(token);
// 2. 设置黑名单过期时间为Token剩余有效时间
stringRedisTemplate.opsForValue().set(
blacklistKey,
"1",
remainingTime,
TimeUnit.SECONDS
);
log.info("用户登出成功Token已加入黑名单");
}
@Override
public UserInfoVo getCurrentUserInfo() {
// 1. 从上下文获取用户ID
Long userId = UserContext.getUserId();
if (userId == null) {
throw new BusinessException("未登录!");
}
// 2. 查询用户信息
User user = userMapper.selectById(userId);
if (user == null) {
throw new BusinessException("用户不存在!");
}
// 3. 构建返回结果
return UserInfoVo.builder()
.id(user.getId())
.username(user.getUsername())
.phone(user.getMobile())
.email(user.getEmail())
.createTime(user.getCreateTime())
.build();
}
}

View File

@@ -52,3 +52,10 @@ knife4j:
logging:
level:
com.genepioneer.mapper: debug
jwt:
secret: bicloud-jwt-secret-key-2024-spring-boot-application-secure-token-generation
access-token-expiration: 7200
refresh-token-expiration: 604800
token-header: Authorization
token-prefix: "Bearer "