From 6f0ee9bbf57fadad13f88cdf919b8e1a0a5e1b01 Mon Sep 17 00:00:00 2001 From: meowrain Date: Wed, 7 Jan 2026 20:16:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0getUserInfo=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=92=8C=E4=BF=AE=E5=A4=8D=E7=9B=B8=E5=85=B3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 /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 --- .../aioj/backend/auth/clients/UserClient.java | 2 +- .../auth/controller/AuthController.java | 13 + .../backend/auth/service/AuthService.java | 9 + .../auth/service/impl/AuthServiceImpl.java | 283 +++++++++++------- .../aioj/backend/auth/utils/JwtUtil.java | 2 +- .../framework/core/errorcode/ErrorCode.java | 52 ++-- .../backend/framework/core/web/Results.java | 2 +- 7 files changed, 221 insertions(+), 142 deletions(-) diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java index 6717dc1..d90ea60 100644 --- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java +++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/clients/UserClient.java @@ -13,6 +13,6 @@ public interface UserClient { Result getUserByUserName(@RequestParam("userAccount") String userAccount); @GetMapping("/inner/get-by-userid") - public Result getUserById(@RequestParam("userId") String userId); + Result getUserById(@RequestParam("userId") String userId); } diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/controller/AuthController.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/controller/AuthController.java index dbb18ea..8a3e1e1 100644 --- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/controller/AuthController.java +++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/controller/AuthController.java @@ -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 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)); + } + } diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/AuthService.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/AuthService.java index d0f7183..03b1864 100644 --- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/AuthService.java +++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/AuthService.java @@ -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 getUserInfo(String accessToken); } diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/impl/AuthServiceImpl.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/impl/AuthServiceImpl.java index ae5a757..8f21f1f 100644 --- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/impl/AuthServiceImpl.java +++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/service/impl/AuthServiceImpl.java @@ -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 userResp = userClient.getUserByUserName(requestParam.getUserAccount()); + // 如果调用user-service失败,那么就说明是系统内部错误 + log.info("正在调用user-service查询用户信息..."); + Result 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 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 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 userResult = userClient.getUserById(userId); - if (userResult.isFail() || userResult.getData() == null) { - log.warn("User not found for id: {}", userId); - return false; - } + // 4. 验证用户是否存在(可选,增加安全性) + Result 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 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; - } - } } diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/utils/JwtUtil.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/utils/JwtUtil.java index b7138e6..db451d6 100644 --- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/utils/JwtUtil.java +++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/utils/JwtUtil.java @@ -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) diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/errorcode/ErrorCode.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/errorcode/ErrorCode.java index 6d74561..5cf1965 100644 --- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/errorcode/ErrorCode.java +++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/errorcode/ErrorCode.java @@ -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; + } } diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/web/Results.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/web/Results.java index 3527409..9aede52 100644 --- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/web/Results.java +++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/web/Results.java @@ -51,7 +51,7 @@ public final class Results { */ public static Result 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().setCode(errorCode).setMessage(errorMessage); }