From 5058c07bffbef7b67ab6683dff034098a3beb908 Mon Sep 17 00:00:00 2001 From: PIG AI Date: Mon, 30 Jun 2025 10:48:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(auth):=20=E4=BC=98=E5=8C=96OAuth2?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E6=A8=A1=E5=9D=97=E6=96=87=E6=A1=A3=E4=B8=8E?= =?UTF-8?q?token=E6=9F=A5=E8=AF=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构OAuth2ResourceOwnerBaseAuthenticationProvider的JavaDoc为中文文档 - 优化token查询接口的分页处理逻辑 - 使用TenantContextHolder替代tenantKeyStrResolver获取租户信息 - 简化token列表查询直接从Redis key解析基础信息 --- .../pigx/auth/endpoint/PigxTokenEndpoint.java | 82 +++++++++---------- ...sourceOwnerBaseAuthenticationProvider.java | 47 +++++------ 2 files changed, 59 insertions(+), 70 deletions(-) diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java index 8ad4bfe08..a21a13702 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java @@ -29,12 +29,11 @@ import com.pig4cloud.pigx.admin.api.vo.TokenVO; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; -import com.pig4cloud.pigx.common.core.util.KeyStrResolver; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.core.util.RetOps; import com.pig4cloud.pigx.common.core.util.SpringContextHolder; +import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; import com.pig4cloud.pigx.common.security.annotation.Inner; -import com.pig4cloud.pigx.common.security.service.PigxUser; import com.pig4cloud.pigx.common.security.util.OAuth2ErrorCodesExpand; import com.pig4cloud.pigx.common.security.util.OAuthClientException; import jakarta.servlet.http.HttpServletRequest; @@ -48,7 +47,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.security.authentication.event.LogoutSuccessEvent; -import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; @@ -83,8 +81,6 @@ public class PigxTokenEndpoint { private final RemoteTenantService tenantService; - private final KeyStrResolver tenantKeyStrResolver; - private final CacheManager cacheManager; /** @@ -172,9 +168,10 @@ public class PigxTokenEndpoint { } /** - * 令牌管理调用 + * 移除指定令牌 * - * @param token token + * @param token 需要移除的令牌 + * @return 操作结果,成功返回true */ @Inner @DeleteMapping("/token/remove/{token}") @@ -199,57 +196,52 @@ public class PigxTokenEndpoint { } /** - * 查询token + * 分页查询token列表 * - * @param params 分页参数 - * @return + * @param params 查询参数,包含分页参数(current,size)和可选用户名过滤条件(username) + * @return 分页结果,包含token列表和总数 */ @Inner @PostMapping("/token/page") public R> tokenList(@RequestBody Map params) { - // 根据分页参数获取对应数据 - String key = String.format("%s::%s::*", tenantKeyStrResolver.key(), CacheConstants.PROJECT_OAUTH_ACCESS); + // 根据username参数获取对应数据 + String username = MapUtil.getStr(params, SecurityConstants.DETAILS_USERNAME); + String usernameKey = String.format("token::username::%s*::*::%s::*", username, TenantContextHolder.getTenantId()); + String key = String.format("token::username::*::*::%s::*", TenantContextHolder.getTenantId()); int current = MapUtil.getInt(params, CommonConstants.CURRENT); int size = MapUtil.getInt(params, CommonConstants.SIZE); - Set keys = redisTemplate.keys(key); + + // 根据是否有username参数选择不同的查询key + String searchKey = StrUtil.isNotBlank(username) ? usernameKey : key; + Set keys = redisTemplate.keys(searchKey); + + // 分页处理 List pages = keys.stream().skip((current - 1) * size).limit(size).toList(); Page result = new Page(current, size); - List tokenVoList = redisTemplate.opsForValue().multiGet(pages).stream().map(obj -> { - OAuth2Authorization authorization = (OAuth2Authorization) obj; + List tokenVoList = pages.stream().map(keyName -> { + // 从key名称解析信息: token::username::{username}::{tenantId}::{tokenId} + String[] keyParts = keyName.split("::"); + if (keyParts.length < 6) { + return null; + } + TokenVO tokenVo = new TokenVO(); - tokenVo.setClientId(authorization.getRegisteredClientId()); - tokenVo.setId(authorization.getId()); - tokenVo.setUsername(authorization.getPrincipalName()); - OAuth2Authorization.Token accessToken = authorization.getAccessToken(); - tokenVo.setAccessToken(accessToken.getToken().getTokenValue()); - - String expiresAt = TemporalAccessorUtil.format(accessToken.getToken().getExpiresAt(), - DatePattern.NORM_DATETIME_PATTERN); + // 从key解析username + String keyUsername = keyParts[2]; + tokenVo.setUsername(keyUsername); + tokenVo.setClientId(keyParts[3]); + tokenVo.setId(keyParts[5]); + tokenVo.setAccessToken(keyParts[5]); + // 获取TTL作为过期时间 + Long ttl = redisTemplate.getExpire(keyName); + // TTL是秒数,转换为过期时间 + long expiresAtMillis = System.currentTimeMillis() + (ttl * 1000); + String expiresAt = TemporalAccessorUtil.format(java.time.Instant.ofEpochMilli(expiresAtMillis), DatePattern.NORM_DATETIME_PATTERN); tokenVo.setExpiresAt(expiresAt); - - String issuedAt = TemporalAccessorUtil.format(accessToken.getToken().getIssuedAt(), - DatePattern.NORM_DATETIME_PATTERN); - tokenVo.setIssuedAt(issuedAt); - - Map attributes = authorization.getAttributes(); - Authentication authentication = (Authentication) attributes.get(Principal.class.getName()); - - if (Objects.isNull(authentication)) { - return tokenVo; - } - - PigxUser pigxUser = (PigxUser) authentication.getPrincipal(); - tokenVo.setUserId(pigxUser.getId()); return tokenVo; - }).filter(tokenVo -> { - // 根据用户名过滤 - String username = MapUtil.getStr(params, SecurityConstants.DETAILS_USERNAME); - if (StrUtil.isBlank(username)) { - return true; - } - return tokenVo.getUsername().contains(username); - }).toList(); + }).filter(Objects::nonNull).toList(); + result.setRecords(tokenVoList); result.setTotal(keys.size()); return R.ok(result); diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/base/OAuth2ResourceOwnerBaseAuthenticationProvider.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/base/OAuth2ResourceOwnerBaseAuthenticationProvider.java index 0e05e2b10..3e747f7de 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/base/OAuth2ResourceOwnerBaseAuthenticationProvider.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/base/OAuth2ResourceOwnerBaseAuthenticationProvider.java @@ -58,11 +58,11 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider refreshTokenGenerator; /** - * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the - * provided parameters. + * 构造一个基于资源所有者密码模式的OAuth2认证提供者 * - * @param authorizationService the authorization service - * @param tokenGenerator the token generator + * @param authenticationManager 认证管理器 + * @param authorizationService 授权服务 + * @param tokenGenerator 令牌生成器 * @since 0.2.3 */ public OAuth2ResourceOwnerBaseAuthenticationProvider(AuthenticationManager authenticationManager, @@ -87,32 +87,28 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider reqParameters); /** - * 当前provider是否支持此令牌类型 + * 判断当前provider是否支持指定的令牌类型 * - * @param authentication - * @return + * @param authentication 要检查的令牌类型 + * @return 如果支持则返回true,否则返回false */ @Override public abstract boolean supports(Class authentication); /** - * 当前的请求客户端是否支持此模式 + * 检查当前请求客户端是否支持此模式 * - * @param registeredClient + * @param registeredClient 已注册的客户端 */ public abstract void checkClient(RegisteredClient registeredClient); /** - * Performs authentication with the same contract as - * {@link AuthenticationManager#authenticate(Authentication)} . + * 执行身份验证,与{@link AuthenticationManager#authenticate(Authentication)}具有相同契约 * - * @param authentication the authentication request object. - * @return a fully authenticated object including credentials. May return - * null if the AuthenticationProvider is unable to support - * authentication of the passed Authentication object. In such a case, - * the next AuthenticationProvider that supports the presented - * Authentication class will be tried. - * @throws AuthenticationException if authentication fails. + * @param authentication 身份验证请求对象 + * @return 包含凭据的完全认证对象,如果无法支持传入的认证对象则可能返回null + * @throws AuthenticationException 身份验证失败时抛出 + * @throws OAuth2AuthenticationException 当scope无效或服务器错误时抛出 */ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { @@ -173,14 +169,15 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider