feat: 添加getUserInfo接口和修复相关bug

- 添加 /getUserInfo 接口,支持根据accessToken获取用户信息
- 修复 JWT subject 从 userAccount 改为 userId
- 修复 Results.failure 方法使用 getErrorMessage() 而非 getMessage()
- 移除 UserClient.getUserById 方法中的 public 修饰符
- 代码格式化:统一缩进和代码风格

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-07 20:16:19 +08:00
parent cacf7ed820
commit 6f0ee9bbf5
7 changed files with 221 additions and 142 deletions

View File

@@ -13,6 +13,6 @@ public interface UserClient {
Result<UserAuthRespDTO> getUserByUserName(@RequestParam("userAccount") String userAccount);
@GetMapping("/inner/get-by-userid")
public Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userId);
Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userId);
}

View File

@@ -1,6 +1,7 @@
package cn.meowrain.aioj.backend.auth.controller;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.oauth2.service.OAuth2SessionService;
import cn.meowrain.aioj.backend.auth.service.AuthService;
@@ -54,4 +55,16 @@ public class AuthController {
return Results.success(isValid);
}
@GetMapping("/getUserInfo")
public Result<UserAuthRespDTO> getUserInfo(@RequestHeader(value = "Authorization", required = false) String authorization) {
String token = null;
if(authorization != null && authorization.startsWith("Bearer ")){
token = authorization.substring(7);
}
if(token != null && sessionService.isTokenBlacklisted(token)) {
return Results.success(null);
}
return Results.success(authService.getUserInfo(token));
}
}

View File

@@ -1,7 +1,9 @@
package cn.meowrain.aioj.backend.auth.service;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.framework.core.web.Result;
public interface AuthService {
@@ -26,4 +28,11 @@ public interface AuthService {
*/
Boolean validateToken(String accessToken);
/**
* 根据accessToken获取用户信息
* @param accessToken
* @return {@link Result<UserAuthRespDTO>}
*/
UserAuthRespDTO getUserInfo(String accessToken);
}

View File

@@ -13,6 +13,7 @@ import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.service.AuthService;
import cn.meowrain.aioj.backend.auth.utils.JwtUtil;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.exception.ClientException;
import cn.meowrain.aioj.backend.framework.core.exception.ServiceException;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import lombok.RequiredArgsConstructor;
@@ -27,145 +28,195 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class AuthServiceImpl implements AuthService {
private final JwtUtil jwtUtil;
private final JwtUtil jwtUtil;
private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;
private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;
private final UserClient userClient;
private final UserClient userClient;
private final StringRedisTemplate stringRedisTemplate;
private final StringRedisTemplate stringRedisTemplate;
private final JwtPropertiesConfiguration jwtPropertiesConfiguration;
private final JwtPropertiesConfiguration jwtPropertiesConfiguration;
@Override
public UserLoginResponseDTO userLogin(UserLoginRequestDTO requestParam) {
log.info("用户登录请求: userAccount={}", requestParam.getUserAccount());
@Override
public UserLoginResponseDTO userLogin(UserLoginRequestDTO requestParam) {
log.info("用户登录请求: userAccount={}", requestParam.getUserAccount());
// 1.校验
userLoginRequestParamVerifyContext.handler(ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName(),
requestParam);
// 1.校验
userLoginRequestParamVerifyContext.handler(ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName(),
requestParam);
// 如果调用user-service失败那么就说明是系统内部错误
log.info("正在调用user-service查询用户信息...");
Result<UserAuthRespDTO> userResp = userClient.getUserByUserName(requestParam.getUserAccount());
// 如果调用user-service失败那么就说明是系统内部错误
log.info("正在调用user-service查询用户信息...");
Result<UserAuthRespDTO> userResp = userClient.getUserByUserName(requestParam.getUserAccount());
if (userResp.isFail()) {
log.error("调用user-service返回失败{}", userResp.getMessage());
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
}
if (userResp.isFail()) {
log.error("调用user-service返回失败{}", userResp.getMessage());
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
}
UserAuthRespDTO user = userResp.getData();
if (user == null) {
log.warn("用户不存在: {}", requestParam.getUserAccount());
throw new ServiceException("用户不存在或密码错误", ErrorCode.NOT_LOGIN_ERROR);
}
UserAuthRespDTO user = userResp.getData();
if (user == null) {
log.warn("用户不存在: {}", requestParam.getUserAccount());
throw new ServiceException("用户不存在或密码错误", ErrorCode.NOT_LOGIN_ERROR);
}
if (!BCrypt.checkpw(requestParam.getUserPassword(), user.getUserPassword())) {
log.warn("密码错误: {}", requestParam.getUserAccount());
throw new ServiceException("用户不存在或密码错误", ErrorCode.NOT_LOGIN_ERROR);
}
if (!BCrypt.checkpw(requestParam.getUserPassword(), user.getUserPassword())) {
log.warn("密码错误: {}", requestParam.getUserAccount());
throw new ServiceException("用户不存在或密码错误", ErrorCode.NOT_LOGIN_ERROR);
}
// 生成 JWT
log.info("正在生成JWT token...");
String accessToken = jwtUtil.generateAccessToken(user);
String refreshToken = jwtUtil.generateRefreshToken(user.getId());
// 生成 JWT
log.info("正在生成JWT token...");
String accessToken = jwtUtil.generateAccessToken(user);
String refreshToken = jwtUtil.generateRefreshToken(user.getId());
UserLoginResponseDTO resp = new UserLoginResponseDTO();
resp.setId(user.getId());
resp.setUserAccount(user.getUserAccount());
resp.setAccessToken(accessToken);
resp.setRefreshToken(refreshToken);
resp.setAccessTokenExpireTime(jwtPropertiesConfiguration.getAccessExpire());
resp.setRefreshTokenExpireTime(jwtPropertiesConfiguration.getRefreshExpire());
// refresh token存入到REDIS里面
stringRedisTemplate.opsForValue()
.set(String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, user.getId()), refreshToken,
jwtPropertiesConfiguration.getRefreshExpire(), TimeUnit.MILLISECONDS);
UserLoginResponseDTO resp = new UserLoginResponseDTO();
resp.setId(user.getId());
resp.setUserAccount(user.getUserAccount());
resp.setAccessToken(accessToken);
resp.setRefreshToken(refreshToken);
resp.setAccessTokenExpireTime(jwtPropertiesConfiguration.getAccessExpire());
resp.setRefreshTokenExpireTime(jwtPropertiesConfiguration.getRefreshExpire());
// refresh token存入到REDIS里面
stringRedisTemplate.opsForValue()
.set(String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, user.getId()), refreshToken,
jwtPropertiesConfiguration.getRefreshExpire(), TimeUnit.MILLISECONDS);
log.info("用户登录成功: userId={}, userAccount={}", user.getId(), user.getUserAccount());
return resp;
}
log.info("用户登录成功: userId={}, userAccount={}", user.getId(), user.getUserAccount());
return resp;
}
/**
* 更新access token使用refresh token
* @param refreshToken
* @return
*/
@Override
public UserLoginResponseDTO refreshToken(String refreshToken) {
UserLoginResponseDTO userLoginResponseDTO = new UserLoginResponseDTO();
if (!jwtUtil.isTokenValid(refreshToken)) {
throw new ServiceException("Refresh Token 已过期");
}
/**
* 更新access token使用refresh token
*
* @param refreshToken
* @return
*/
@Override
public UserLoginResponseDTO refreshToken(String refreshToken) {
UserLoginResponseDTO userLoginResponseDTO = new UserLoginResponseDTO();
if (!jwtUtil.isTokenValid(refreshToken)) {
throw new ServiceException("Refresh Token 已过期");
}
Long userId = Long.valueOf(jwtUtil.parseClaims(refreshToken).getSubject());
Long userId = Long.valueOf(jwtUtil.parseClaims(refreshToken).getSubject());
String cacheKey = String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, userId);
String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
String cacheKey = String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, userId);
String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null || !cacheValue.equals(refreshToken)) {
throw new ServiceException("Refresh Token 已失效");
}
if (cacheValue == null || !cacheValue.equals(refreshToken)) {
throw new ServiceException("Refresh Token 已失效");
}
// 再次签发新的 Access Token
// 此处你需要查用户,拿 userName, role
Result<UserAuthRespDTO> userResult = userClient.getUserById(String.valueOf(userId));
if (userResult.isFail()) {
log.error("通过id查找用户失败:{}", userResult.getMessage());
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
}
UserAuthRespDTO user = userResult.getData();
String newAccessToken = jwtUtil.generateAccessToken(user);
// 再次签发新的 Access Token
// 此处你需要查用户,拿 userName, role
Result<UserAuthRespDTO> userResult = userClient.getUserById(String.valueOf(userId));
if (userResult.isFail()) {
log.error("通过id查找用户失败:{}", userResult.getMessage());
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
}
UserAuthRespDTO user = userResult.getData();
String newAccessToken = jwtUtil.generateAccessToken(user);
// 设置refresh token和access token
userLoginResponseDTO.setRefreshToken(refreshToken);
userLoginResponseDTO.setAccessToken(newAccessToken);
userLoginResponseDTO.setAccessTokenExpireTime(jwtPropertiesConfiguration.getAccessExpire());
userLoginResponseDTO.setRefreshTokenExpireTime(jwtPropertiesConfiguration.getRefreshExpire());
return userLoginResponseDTO;
}
// 设置refresh token和access token
userLoginResponseDTO.setRefreshToken(refreshToken);
userLoginResponseDTO.setAccessToken(newAccessToken);
userLoginResponseDTO.setAccessTokenExpireTime(jwtPropertiesConfiguration.getAccessExpire());
userLoginResponseDTO.setRefreshTokenExpireTime(jwtPropertiesConfiguration.getRefreshExpire());
return userLoginResponseDTO;
}
/**
* 验证token的有效性
* @param accessToken 访问令牌
* @return token是否有效
*/
@Override
public Boolean validateToken(String accessToken) {
try {
// 1. 检查token格式
if (accessToken == null || accessToken.trim().isEmpty()) {
log.warn("Access token is null or empty");
return false;
}
/**
* 验证token的有效性
*
* @param accessToken 访问令牌
* @return token是否有效
*/
@Override
public Boolean validateToken(String accessToken) {
try {
// 1. 检查token格式
if (accessToken == null || accessToken.trim().isEmpty()) {
log.warn("Access token is null or empty");
return false;
}
// 2. 验证token签名和过期时间
if (!jwtUtil.isTokenValid(accessToken)) {
log.warn("Access token is invalid or expired");
return false;
}
// 2. 验证token签名和过期时间
if (!jwtUtil.isTokenValid(accessToken)) {
log.warn("Access token is invalid or expired");
return false;
}
// 3. 解析token获取用户信息
String userId = jwtUtil.parseClaims(accessToken).getSubject();
if (userId == null) {
log.warn("Access token does not contain valid user id");
return false;
}
// 3. 解析token获取用户信息
String userId = jwtUtil.parseClaims(accessToken).getSubject();
if (userId == null) {
log.warn("Access token does not contain valid user id");
return false;
}
// 4. 验证用户是否存在(可选,增加安全性)
Result<UserAuthRespDTO> userResult = userClient.getUserById(userId);
if (userResult.isFail() || userResult.getData() == null) {
log.warn("User not found for id: {}", userId);
return false;
}
// 4. 验证用户是否存在(可选,增加安全性)
Result<UserAuthRespDTO> userResult = userClient.getUserById(userId);
if (userResult.isFail() || userResult.getData() == null) {
log.warn("User not found for id: {}", userId);
return false;
}
log.debug("Access token validation successful for user: {}", userId);
return true;
} catch (Exception e) {
log.error("Error validating access token", e);
return false;
}
}
@Override
public UserAuthRespDTO getUserInfo(String accessToken) {
// 1. 参数校验
if (accessToken == null || accessToken.isBlank()) {
log.warn("Access token is null or empty");
throw new ClientException(ErrorCode.PARAMS_ERROR);
}
// 2. token 校验
if (!jwtUtil.isTokenValid(accessToken)) {
log.warn("Access token is invalid or expired");
throw new ClientException(ErrorCode.NOT_LOGIN_ERROR);
}
// 3. 解析 token
String userId;
try {
userId = jwtUtil.parseClaims(accessToken).getSubject();
} catch (Exception e) {
log.warn("Failed to parse access token", e);
throw new ClientException(ErrorCode.NOT_LOGIN_ERROR);
}
if (userId == null) {
throw new ClientException(ErrorCode.NOT_LOGIN_ERROR);
}
// 4. 查询用户信息IO 操作)
Result<UserAuthRespDTO> userResult;
try {
userResult = userClient.getUserById(userId);
} catch (Exception e) {
log.error("Failed to call user service", e);
throw new ClientException(ErrorCode.SYSTEM_ERROR);
}
if (userResult == null || userResult.isFail()) {
throw new ClientException(ErrorCode.SYSTEM_ERROR);
}
if (userResult.getData() == null) {
throw new ClientException(ErrorCode.NOT_FOUND_ERROR);
}
return userResult.getData();
}
log.debug("Access token validation successful for user: {}", userId);
return true;
}
catch (Exception e) {
log.error("Error validating access token", e);
return false;
}
}
}

View File

@@ -33,7 +33,7 @@ public class JwtUtil {
claims.put("role", user.getUserRole());
return Jwts.builder()
.subject(user.getUserAccount())
.subject(String.valueOf(user.getId()))
.issuedAt(new Date(now))
.expiration(new Date(now + jwtConfig.getAccessExpire()))
.claims(claims)

View File

@@ -2,34 +2,40 @@ package cn.meowrain.aioj.backend.framework.core.errorcode;
public enum ErrorCode implements IErrorCode {
SUCCESS("0", "ok"), PARAMS_ERROR("40000", "请求参数错误"), NOT_LOGIN_ERROR("40100", "未登录"), NO_AUTH_ERROR("40101", "无权限"),
NOT_FOUND_ERROR("40400", "请求数据不存在"), FORBIDDEN_ERROR("40300", "禁止访问"), SYSTEM_ERROR("50000", "系统内部异常"),
OPERATION_ERROR("50001", "操作失败"), API_REQUEST_ERROR("50010", "接口调用失败");
SUCCESS("0", "ok"),
PARAMS_ERROR("40000", "请求参数错误"),
NOT_LOGIN_ERROR("40100", "未登录"),
NO_AUTH_ERROR("40101", "无权限"),
NOT_FOUND_ERROR("40400", "请求数据不存在"),
FORBIDDEN_ERROR("40300", "禁止访问"),
SYSTEM_ERROR("50000", "系统内部异常"),
OPERATION_ERROR("50001", "操作失败"),
API_REQUEST_ERROR("50010", "接口调用失败");
/**
* 状态码
*/
/**
* 状态码
*/
private final String code;
private final String code;
/**
* 信息
*/
private final String message;
/**
* 信息
*/
private final String message;
ErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
ErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
@Override
public String code() {
return code;
}
@Override
public String code() {
return code;
}
@Override
public String message() {
return message;
}
@Override
public String message() {
return message;
}
}

View File

@@ -51,7 +51,7 @@ public final class Results {
*/
public static Result<Void> failure(AbstractException exception) {
String errorCode = Optional.ofNullable(exception.getErrorCode()).orElse(ErrorCode.SYSTEM_ERROR.code());
String errorMessage = Optional.ofNullable(exception.getMessage()).orElse(ErrorCode.SYSTEM_ERROR.message());
String errorMessage = Optional.ofNullable(exception.getErrorMessage()).orElse(ErrorCode.SYSTEM_ERROR.message());
return new Result<Void>().setCode(errorCode).setMessage(errorMessage);
}