From c8edf16536b6c4fd098d826ef3f38055a0e24206 Mon Sep 17 00:00:00 2001 From: PIG AI Date: Thu, 3 Jul 2025 16:30:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(security):=20=E9=87=8D=E6=9E=84Redis?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E7=9A=84OAuth2=E6=8E=88=E6=9D=83=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将PigxRedisOAuth2AuthorizationService迁移至common-security模块 - 使用RedisTemplate替代RedisUtils进行缓存操作 - 增加Jackson序列化支持RouteDefinitionVo - 优化token存储逻辑与过期时间处理 - 统一授权服务与路由管理的Redis配置 --- ...sourceOwnerBaseAuthenticationProvider.java | 2 +- ...edisOAuth2AuthorizationConsentService.java | 18 ++++--- .../PigxRedisOAuth2AuthorizationService.java | 51 +++++++++++-------- ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../admin/config/DynamicRouteInitRunner.java | 22 ++++---- 5 files changed, 57 insertions(+), 38 deletions(-) rename {pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store => pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service}/PigxRedisOAuth2AuthorizationConsentService.java (71%) rename {pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store => pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service}/PigxRedisOAuth2AuthorizationService.java (77%) 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 c5227243a..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 @@ -2,8 +2,8 @@ package com.pig4cloud.pigx.auth.support.base; import cn.hutool.core.util.BooleanUtil; import cn.hutool.extra.spring.SpringUtil; -import com.pig4cloud.pigx.auth.support.store.PigxRedisOAuth2AuthorizationService; import com.pig4cloud.pigx.common.core.constant.CommonConstants; +import com.pig4cloud.pigx.common.security.service.PigxRedisOAuth2AuthorizationService; import com.pig4cloud.pigx.common.security.util.OAuth2ErrorCodesExpand; import com.pig4cloud.pigx.common.security.util.ScopeException; import lombok.extern.slf4j.Slf4j; diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store/PigxRedisOAuth2AuthorizationConsentService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationConsentService.java similarity index 71% rename from pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store/PigxRedisOAuth2AuthorizationConsentService.java rename to pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationConsentService.java index d2f8f8225..429cb4f8a 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store/PigxRedisOAuth2AuthorizationConsentService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationConsentService.java @@ -1,37 +1,41 @@ -package com.pig4cloud.pigx.auth.support.store; +package com.pig4cloud.pigx.common.security.service; -import com.pig4cloud.pigx.common.data.cache.RedisUtils; import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; -import org.springframework.stereotype.Service; import org.springframework.util.Assert; -@Service +import java.util.concurrent.TimeUnit; + @RequiredArgsConstructor public class PigxRedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService { + private final RedisTemplate redisTemplate; + private final static Long TIMEOUT = 10L; @Override public void save(OAuth2AuthorizationConsent authorizationConsent) { Assert.notNull(authorizationConsent, "authorizationConsent cannot be null"); - RedisUtils.set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT * 60); + redisTemplate.opsForValue() + .set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT, TimeUnit.MINUTES); } @Override public void remove(OAuth2AuthorizationConsent authorizationConsent) { Assert.notNull(authorizationConsent, "authorizationConsent cannot be null"); - RedisUtils.delete(buildKey(authorizationConsent)); + redisTemplate.delete(buildKey(authorizationConsent)); } @Override public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) { Assert.hasText(registeredClientId, "registeredClientId cannot be empty"); Assert.hasText(principalName, "principalName cannot be empty"); - return RedisUtils.get(buildKey(registeredClientId, principalName)); + return (OAuth2AuthorizationConsent) redisTemplate.opsForValue() + .get(buildKey(registeredClientId, principalName)); } private static String buildKey(String registeredClientId, String principalName) { diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store/PigxRedisOAuth2AuthorizationService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java similarity index 77% rename from pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store/PigxRedisOAuth2AuthorizationService.java rename to pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java index d3dd201f3..2d26c0205 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/store/PigxRedisOAuth2AuthorizationService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java @@ -1,10 +1,10 @@ -package com.pig4cloud.pigx.auth.support.store; +package com.pig4cloud.pigx.common.security.service; import cn.hutool.core.collection.CollUtil; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; -import com.pig4cloud.pigx.common.data.cache.RedisUtils; -import com.pig4cloud.pigx.common.security.service.PigxUser; import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.lang.Nullable; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -15,7 +15,6 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; -import org.springframework.stereotype.Service; import org.springframework.util.Assert; import java.security.Principal; @@ -24,14 +23,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.TimeUnit; /** - * 基于Redis实现的OAuth2授权服务类,提供授权信息的存储、查询和删除功能 - * * @author lengleng - * @date 2025/06/30 + * @date 2022/5/27 */ -@Service @RequiredArgsConstructor public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationService { @@ -39,6 +36,8 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS private static final String AUTHORIZATION = "token"; + private final RedisTemplate redisTemplate; + private static boolean isState(OAuth2Authorization authorization) { return Objects.nonNull(authorization.getAttribute("state")); } @@ -63,7 +62,9 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS if (isState(authorization)) { String token = authorization.getAttribute("state"); - RedisUtils.set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT * 60); + redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, TimeUnit.MINUTES); } if (isCode(authorization)) { @@ -72,20 +73,28 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(), authorizationCodeToken.getExpiresAt()); - RedisUtils.set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), authorization, - (int) (between * 60)); + 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()); - RedisUtils.set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), authorization, (int) between); + 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()); - RedisUtils.set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, (int) between); + redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between, + TimeUnit.SECONDS); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = authorization.getAttribute(Principal.class.getName()); @@ -93,7 +102,7 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS // 扩展记录 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()); - RedisUtils.set(tokenUsername, accessToken.getTokenValue(), (int) between); + redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS); } } } @@ -130,7 +139,7 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS keys.add(key); } - RedisUtils.delete(keys.toArray(new String[0])); + redisTemplate.delete(keys); } @Override @@ -143,7 +152,9 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS @Nullable public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) { Assert.hasText(token, "token cannot be empty"); - return RedisUtils.get(buildKey(Objects.requireNonNullElse(tokenType, OAuth2TokenType.ACCESS_TOKEN).getValue(), token)); + 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) { @@ -162,16 +173,16 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx String tokenUsernameKey = String.format("%s::%s::%s::*", AUTHORIZATION, SecurityConstants.DETAILS_USERNAME, authenticationName); - Set keys = RedisUtils.keys(tokenUsernameKey); + Set keys = redisTemplate.keys(tokenUsernameKey); if (CollUtil.isEmpty(keys)) { return; } - List tokenList = RedisUtils.multiGet(keys.stream().toList()); + List tokenList = redisTemplate.opsForValue().multiGet(keys); - for (String token : tokenList) { + for (Object token : tokenList) { // 根据token 查询存储的 OAuth2Authorization - OAuth2Authorization authorization = this.findByToken(token, OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization authorization = this.findByToken((String) token, OAuth2TokenType.ACCESS_TOKEN); // 根据 OAuth2Authorization 删除相关令牌 this.remove(authorization); } diff --git a/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 3985faa40..eac267c60 100644 --- a/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -2,6 +2,8 @@ com.pig4cloud.pigx.common.security.service.PigxDefaultUserDetailsServiceImpl com.pig4cloud.pigx.common.security.service.PigxTocDefaultUserDetailsServiceImpl com.pig4cloud.pigx.common.security.service.PigxMobileUserDetailServiceImpl com.pig4cloud.pigx.common.security.service.PigxTocMobileUserDetailsServiceImpl +com.pig4cloud.pigx.common.security.service.PigxRedisOAuth2AuthorizationService +com.pig4cloud.pigx.common.security.service.PigxRedisOAuth2AuthorizationConsentService com.pig4cloud.pigx.common.security.component.PigxSecurityInnerAspect com.pig4cloud.pigx.common.security.component.PigxSecurityMessageSourceConfiguration com.pig4cloud.pigx.common.security.service.PigxRemoteRegisteredClientRepository diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/config/DynamicRouteInitRunner.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/config/DynamicRouteInitRunner.java index 745585c41..56c9e0440 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/config/DynamicRouteInitRunner.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/config/DynamicRouteInitRunner.java @@ -21,7 +21,6 @@ import cn.hutool.json.JSONArray; import cn.hutool.json.JSONUtil; import com.pig4cloud.pigx.admin.service.SysRouteConfService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; -import com.pig4cloud.pigx.common.data.cache.RedisUtils; import com.pig4cloud.pigx.common.gateway.support.DynamicRouteInitEvent; import com.pig4cloud.pigx.common.gateway.vo.RouteDefinitionVo; import lombok.RequiredArgsConstructor; @@ -34,9 +33,11 @@ import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; -import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.scheduling.annotation.Async; import org.springframework.transaction.event.TransactionalEventListener; @@ -44,10 +45,10 @@ import java.net.URI; import java.util.Map; /** - * 动态路由初始化运行器:容器启动后保存配置文件里面的路由信息到Redis - * * @author lengleng * @date 2018/10/31 + *

+ * 容器启动后保存配置文件里面的路由信息到Redis */ @Slf4j @Configuration @@ -55,6 +56,8 @@ import java.util.Map; @ConditionalOnDiscoveryEnabled public class DynamicRouteInitRunner implements InitializingBean { + private final RedisTemplate redisTemplate; + private final SysRouteConfService routeConfService; private final RedisMessageListenerContainer listenerContainer; @@ -73,7 +76,7 @@ public class DynamicRouteInitRunner implements InitializingBean { @Order @TransactionalEventListener({DynamicRouteInitEvent.class}) public void initRoute() { - RedisUtils.delete(CacheConstants.ROUTE_KEY); + redisTemplate.delete(CacheConstants.ROUTE_KEY); log.info("开始初始化网关路由"); routeConfService.list().forEach(route -> { @@ -90,14 +93,13 @@ public class DynamicRouteInitRunner implements InitializingBean { vo.setMetadata(JSONUtil.toBean(route.getMetadata(), Map.class)); log.info("加载路由ID:{},{}", route.getRouteId(), vo); - RedisUtils.hset(CacheConstants.ROUTE_KEY, route.getRouteId(), vo); + redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class)); + HashOperations stringStringValueOperations = redisTemplate.opsForHash(); + stringStringValueOperations.put(CacheConstants.ROUTE_KEY, route.getRouteId(), vo); }); // 通知网关重置路由 - RedisUtils.execute((RedisCallback) connection -> { - connection.publish(CacheConstants.ROUTE_JVM_RELOAD_TOPIC.getBytes(), "路由信息,网关缓存更新".getBytes()); - return null; - }); + redisTemplate.convertAndSend(CacheConstants.ROUTE_JVM_RELOAD_TOPIC, "路由信息,网关缓存更新"); log.debug("初始化网关路由结束 "); }