From 9649b980e56eb43f4c3981fc55bb4bdbdadf412f Mon Sep 17 00:00:00 2001 From: PIG AI Date: Wed, 9 Jul 2025 21:16:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(security):=20=E5=A2=9E=E5=BC=BAOAuth2=20tok?= =?UTF-8?q?en=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD,=20=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=93=8D=E4=BD=9C=E5=90=8E=E6=B8=85=E7=90=86?= =?UTF-8?q?=E6=AE=8B=E7=95=99=E7=9A=84username=E5=85=B3=E8=81=94=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PigxRedisOAuth2AuthorizationService.java | 267 +++++++++--------- 1 file changed, 139 insertions(+), 128 deletions(-) diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java index 2d26c0205..301745fd3 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java @@ -32,161 +32,172 @@ import java.util.concurrent.TimeUnit; @RequiredArgsConstructor public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationService { - private final static Long TIMEOUT = 10L; + private final static Long TIMEOUT = 10L; - private static final String AUTHORIZATION = "token"; + private static final String AUTHORIZATION = "token"; - private final RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; - private static boolean isState(OAuth2Authorization authorization) { - return Objects.nonNull(authorization.getAttribute("state")); - } + private static boolean isState(OAuth2Authorization authorization) { + return Objects.nonNull(authorization.getAttribute("state")); + } - private static boolean isCode(OAuth2Authorization authorization) { - OAuth2Authorization.Token authorizationCode = authorization - .getToken(OAuth2AuthorizationCode.class); - return Objects.nonNull(authorizationCode); - } + private static boolean isCode(OAuth2Authorization authorization) { + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); + return Objects.nonNull(authorizationCode); + } - private static boolean isRefreshToken(OAuth2Authorization authorization) { - return Objects.nonNull(authorization.getRefreshToken()); - } + private static boolean isRefreshToken(OAuth2Authorization authorization) { + return Objects.nonNull(authorization.getRefreshToken()); + } - private static boolean isAccessToken(OAuth2Authorization authorization) { - return Objects.nonNull(authorization.getAccessToken()); - } + private static boolean isAccessToken(OAuth2Authorization authorization) { + return Objects.nonNull(authorization.getAccessToken()); + } - @Override - public void save(OAuth2Authorization authorization) { - Assert.notNull(authorization, "authorization cannot be null"); + @Override + public void save(OAuth2Authorization authorization) { + Assert.notNull(authorization, "authorization cannot be null"); - if (isState(authorization)) { - String token = authorization.getAttribute("state"); - redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue() - .set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, TimeUnit.MINUTES); - } + if (isState(authorization)) { + String token = authorization.getAttribute("state"); + redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, TimeUnit.MINUTES); + } - if (isCode(authorization)) { - OAuth2Authorization.Token authorizationCode = authorization - .getToken(OAuth2AuthorizationCode.class); - OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); - long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(), - authorizationCodeToken.getExpiresAt()); - redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue() - .set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), authorization, - between, TimeUnit.MINUTES); - } + if (isCode(authorization)) { + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); + OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); + long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(), + authorizationCodeToken.getExpiresAt()); + redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), authorization, + between, TimeUnit.MINUTES); + } - if (isRefreshToken(authorization)) { - OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); - long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt()); - redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue() - .set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), authorization, between, - TimeUnit.SECONDS); - } + if (isRefreshToken(authorization)) { + OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); + long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt()); + redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), authorization, between, + TimeUnit.SECONDS); + } - if (isAccessToken(authorization)) { - OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); - long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()); - redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue() - .set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between, - TimeUnit.SECONDS); + if (isAccessToken(authorization)) { + OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); + long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()); + redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between, + TimeUnit.SECONDS); - UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = authorization.getAttribute(Principal.class.getName()); + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = authorization + .getAttribute(Principal.class.getName()); - if (Objects.nonNull(usernamePasswordAuthenticationToken) && usernamePasswordAuthenticationToken.getPrincipal() instanceof PigxUser pigxUser) { - // 扩展记录 access-token 、username 的关系 token::username::admin::tenantId::xxx - String tokenUsername = String.format("%s::%s::%s::%s::%s::%s", AUTHORIZATION, - SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), authorization.getRegisteredClientId(), pigxUser.getTenantId(), accessToken.getTokenValue()); - redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS); - } - } - } + if (Objects.nonNull(usernamePasswordAuthenticationToken) + && usernamePasswordAuthenticationToken.getPrincipal() instanceof PigxUser pigxUser) { + // 扩展记录 access-token 、username 的关系 token::username::admin::tenantId::xxx + String tokenUsername = String.format("%s::%s::%s::%s::%s::%s", AUTHORIZATION, + SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), + authorization.getRegisteredClientId(), pigxUser.getTenantId(), accessToken.getTokenValue()); + redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS); + } + } + } - @Override - public void remove(OAuth2Authorization authorization) { - Assert.notNull(authorization, "authorization cannot be null"); + @Override + public void remove(OAuth2Authorization authorization) { + Assert.notNull(authorization, "authorization cannot be null"); - List keys = new ArrayList<>(); - if (isState(authorization)) { - String token = authorization.getAttribute("state"); - keys.add(buildKey(OAuth2ParameterNames.STATE, token)); - } + List keys = new ArrayList<>(); + if (isState(authorization)) { + String token = authorization.getAttribute("state"); + keys.add(buildKey(OAuth2ParameterNames.STATE, token)); + } - if (isCode(authorization)) { - OAuth2Authorization.Token authorizationCode = authorization - .getToken(OAuth2AuthorizationCode.class); - OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); - keys.add(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue())); - } + if (isCode(authorization)) { + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); + OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); + keys.add(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue())); + } - if (isRefreshToken(authorization)) { - OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); - keys.add(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue())); - } + if (isRefreshToken(authorization)) { + OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); + keys.add(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue())); + } - if (isAccessToken(authorization)) { - OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); - keys.add(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue())); + if (isAccessToken(authorization)) { + OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); + keys.add(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue())); - // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx - String key = String.format("%s::%s::%s::%s::*::%s", AUTHORIZATION, - SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), authorization.getRegisteredClientId(), accessToken.getTokenValue()); - keys.add(key); - } + // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx + String key = String.format("%s::%s::%s::%s::*::%s", AUTHORIZATION, SecurityConstants.DETAILS_USERNAME, + authorization.getPrincipalName(), authorization.getRegisteredClientId(), + accessToken.getTokenValue()); + Set pattenKey = redisTemplate.keys(key); + if (CollUtil.isNotEmpty(pattenKey)) { + keys.addAll(pattenKey); + } + } - redisTemplate.delete(keys); - } + redisTemplate.delete(keys); + } - @Override - @Nullable - public OAuth2Authorization findById(String id) { - throw new UnsupportedOperationException(); - } + @Override + @Nullable + public OAuth2Authorization findById(String id) { + throw new UnsupportedOperationException(); + } - @Override - @Nullable - public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) { - Assert.hasText(token, "token cannot be empty"); - redisTemplate.setValueSerializer(RedisSerializer.java()); - return (OAuth2Authorization) redisTemplate.opsForValue() - .get(buildKey(Objects.requireNonNullElse(tokenType, OAuth2TokenType.ACCESS_TOKEN).getValue(), token)); - } + @Override + @Nullable + public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) { + Assert.hasText(token, "token cannot be empty"); + redisTemplate.setValueSerializer(RedisSerializer.java()); + return (OAuth2Authorization) redisTemplate.opsForValue() + .get(buildKey(Objects.requireNonNullElse(tokenType, OAuth2TokenType.ACCESS_TOKEN).getValue(), token)); + } - private String buildKey(String type, String id) { - return String.format("%s::%s::%s", AUTHORIZATION, type, id); - } + private String buildKey(String type, String id) { + return String.format("%s::%s::%s", AUTHORIZATION, type, id); + } - /** - * 根据用户名移除相关授权信息 - * - * @param authentication 认证信息,包含用户名 - */ - public void removeByUsername(Authentication authentication) { - // 根据 username查询对应access-token - String authenticationName = authentication.getName(); + /** + * 根据用户名移除相关授权信息 + * @param authentication 认证信息,包含用户名 + */ + public void removeByUsername(Authentication authentication) { + // 根据 username查询对应access-token + String authenticationName = authentication.getName(); - // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx - String tokenUsernameKey = String.format("%s::%s::%s::*", AUTHORIZATION, - SecurityConstants.DETAILS_USERNAME, authenticationName); - Set keys = redisTemplate.keys(tokenUsernameKey); - if (CollUtil.isEmpty(keys)) { - return; - } + // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx + String tokenUsernameKey = String.format("%s::%s::%s::*", AUTHORIZATION, SecurityConstants.DETAILS_USERNAME, + authenticationName); + Set keys = redisTemplate.keys(tokenUsernameKey); + if (CollUtil.isEmpty(keys)) { + return; + } - List tokenList = redisTemplate.opsForValue().multiGet(keys); + List tokenList = redisTemplate.opsForValue().multiGet(keys); - for (Object token : tokenList) { - // 根据token 查询存储的 OAuth2Authorization - OAuth2Authorization authorization = this.findByToken((String) token, OAuth2TokenType.ACCESS_TOKEN); - // 根据 OAuth2Authorization 删除相关令牌 - this.remove(authorization); - } + for (Object token : tokenList) { + // 根据token 查询存储的 OAuth2Authorization + OAuth2Authorization authorization = this.findByToken((String) token, OAuth2TokenType.ACCESS_TOKEN); - } + if (Objects.isNull(authorization)) { + continue; + } + // 根据 OAuth2Authorization 删除相关令牌 + this.remove(authorization); + } + + redisTemplate.delete(keys); + } }