diff --git a/src/main/java/com/rabbitmqprac/config/RedisCacheConfig.java b/src/main/java/com/rabbitmqprac/config/RedisCacheConfig.java new file mode 100644 index 0000000..70d2021 --- /dev/null +++ b/src/main/java/com/rabbitmqprac/config/RedisCacheConfig.java @@ -0,0 +1,115 @@ +package com.rabbitmqprac.config; + +import com.rabbitmqprac.global.annotation.InfraRedisConnectionFactory; +import com.rabbitmqprac.global.annotation.OidcCacheManager; +import com.rabbitmqprac.global.annotation.SecurityUserCacheManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.cache.CacheKeyPrefix; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +@Configuration +@EnableCaching +public class RedisCacheConfig { + @Value("${spring.data.redis.host}") + private String host; + @Value("${spring.data.redis.port}") + private int port; + + private final long defaultCacheTtlSec = 60; + private final long securityUserCacheTtlSec = 30; + private final long oidcCacheTtlDay = 3; + + @Bean + @Primary + @InfraRedisConnectionFactory + public RedisConnectionFactory infraRedisConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port); + LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder().build(); + return new LettuceConnectionFactory(config, clientConfig); + } + + /** + * CacheManager를 명시하지 않을 경우 default로 사용되는 CacheManager + */ + @Bean + @Primary + public CacheManager defaultCacheManager(@InfraRedisConnectionFactory RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .disableCachingNullValues() + .computePrefixWith(CacheKeyPrefix.simple()) + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()) + ) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()) + ) + .entryTtl(Duration.ofSeconds(defaultCacheTtlSec)); + + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactory) + .cacheDefaults(config) + .build(); + } + + @Bean + @SecurityUserCacheManager + public CacheManager securityUserCacheManager(@InfraRedisConnectionFactory RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .disableCachingNullValues() + .computePrefixWith(CacheKeyPrefix.simple()) + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer() + ) + ) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new GenericJackson2JsonRedisSerializer() + ) + ) + .entryTtl(Duration.ofSeconds(securityUserCacheTtlSec)); + + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactory) + .cacheDefaults(config) + .build(); + } + + @Bean + @OidcCacheManager + public CacheManager oidcCacheManager(@InfraRedisConnectionFactory RedisConnectionFactory cf) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .disableCachingNullValues() + .computePrefixWith(CacheKeyPrefix.simple()) + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer() + )) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new GenericJackson2JsonRedisSerializer() + )) + .entryTtl(Duration.ofDays(oidcCacheTtlDay)); + + return RedisCacheManager + .RedisCacheManagerBuilder + .fromConnectionFactory(cf) + .cacheDefaults(config) + .build(); + } +} diff --git a/src/main/java/com/rabbitmqprac/domain/context/auth/service/UserDetailServiceImpl.java b/src/main/java/com/rabbitmqprac/domain/context/auth/service/UserDetailServiceImpl.java index a29b2f1..292afed 100644 --- a/src/main/java/com/rabbitmqprac/domain/context/auth/service/UserDetailServiceImpl.java +++ b/src/main/java/com/rabbitmqprac/domain/context/auth/service/UserDetailServiceImpl.java @@ -4,6 +4,7 @@ import com.rabbitmqprac.domain.persistence.user.entity.User; import com.rabbitmqprac.infra.security.authentication.SecurityUserDetails; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.Primary; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -17,6 +18,7 @@ public class UserDetailServiceImpl implements UserDetailsService { private final UserService userService; @Override + @Cacheable(value = "securityUser", key = "#userId", unless = "#result == null", cacheManager = "securityUserCacheManager") public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { User user = userService.readUser(Long.parseLong(userId)); return SecurityUserDetails.from(user); diff --git a/src/main/java/com/rabbitmqprac/global/annotation/InfraRedisConnectionFactory.java b/src/main/java/com/rabbitmqprac/global/annotation/InfraRedisConnectionFactory.java new file mode 100644 index 0000000..16e9608 --- /dev/null +++ b/src/main/java/com/rabbitmqprac/global/annotation/InfraRedisConnectionFactory.java @@ -0,0 +1,17 @@ +package com.rabbitmqprac.global.annotation; + +import org.springframework.beans.factory.annotation.Qualifier; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, + ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier("infraRedisConnectionFactory") +public @interface InfraRedisConnectionFactory { +} diff --git a/src/main/java/com/rabbitmqprac/global/annotation/OidcCacheManager.java b/src/main/java/com/rabbitmqprac/global/annotation/OidcCacheManager.java new file mode 100644 index 0000000..3fb9504 --- /dev/null +++ b/src/main/java/com/rabbitmqprac/global/annotation/OidcCacheManager.java @@ -0,0 +1,18 @@ +package com.rabbitmqprac.global.annotation; + +import com.rabbitmqprac.global.constant.CacheManagerType; +import org.springframework.beans.factory.annotation.Qualifier; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, + ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier(CacheManagerType.OIDC) +public @interface OidcCacheManager { +} diff --git a/src/main/java/com/rabbitmqprac/global/annotation/SecurityUserCacheManager.java b/src/main/java/com/rabbitmqprac/global/annotation/SecurityUserCacheManager.java new file mode 100644 index 0000000..dd7d79f --- /dev/null +++ b/src/main/java/com/rabbitmqprac/global/annotation/SecurityUserCacheManager.java @@ -0,0 +1,19 @@ +package com.rabbitmqprac.global.annotation; + +import com.rabbitmqprac.global.constant.CacheManagerType; +import org.springframework.beans.factory.annotation.Qualifier; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, + ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier(CacheManagerType.SECURITY_USER) +public @interface SecurityUserCacheManager { +} + diff --git a/src/main/java/com/rabbitmqprac/global/constant/CacheManagerType.java b/src/main/java/com/rabbitmqprac/global/constant/CacheManagerType.java new file mode 100644 index 0000000..1771477 --- /dev/null +++ b/src/main/java/com/rabbitmqprac/global/constant/CacheManagerType.java @@ -0,0 +1,6 @@ +package com.rabbitmqprac.global.constant; + +public final class CacheManagerType { + public static final String SECURITY_USER = "securityUserCacheManager"; + public static final String OIDC = "oidcCacheManager"; +} diff --git a/src/main/java/com/rabbitmqprac/global/consant/SessionType.java b/src/main/java/com/rabbitmqprac/global/constant/SessionType.java similarity index 78% rename from src/main/java/com/rabbitmqprac/global/consant/SessionType.java rename to src/main/java/com/rabbitmqprac/global/constant/SessionType.java index e44e46b..a803651 100644 --- a/src/main/java/com/rabbitmqprac/global/consant/SessionType.java +++ b/src/main/java/com/rabbitmqprac/global/constant/SessionType.java @@ -1,4 +1,4 @@ -package com.rabbitmqprac.global.consant; +package com.rabbitmqprac.global.constant; public final class SessionType { public static final String START_TIME = "startTime"; diff --git a/src/main/java/com/rabbitmqprac/global/interceptor/RequestInterceptor.java b/src/main/java/com/rabbitmqprac/global/interceptor/RequestInterceptor.java index 0c9fe9b..7cad7aa 100644 --- a/src/main/java/com/rabbitmqprac/global/interceptor/RequestInterceptor.java +++ b/src/main/java/com/rabbitmqprac/global/interceptor/RequestInterceptor.java @@ -1,6 +1,6 @@ package com.rabbitmqprac.global.interceptor; -import com.rabbitmqprac.global.consant.SessionType; +import com.rabbitmqprac.global.constant.SessionType; import com.rabbitmqprac.global.util.RequestInfoExtractor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/src/main/java/com/rabbitmqprac/global/util/RequestInfoExtractor.java b/src/main/java/com/rabbitmqprac/global/util/RequestInfoExtractor.java index 6d7dee0..0e6cc19 100644 --- a/src/main/java/com/rabbitmqprac/global/util/RequestInfoExtractor.java +++ b/src/main/java/com/rabbitmqprac/global/util/RequestInfoExtractor.java @@ -1,7 +1,7 @@ package com.rabbitmqprac.global.util; import com.rabbitmqprac.global.annotation.Util; -import com.rabbitmqprac.global.consant.SessionType; +import com.rabbitmqprac.global.constant.SessionType; import jakarta.servlet.http.HttpServletRequest; import org.apache.logging.log4j.util.Strings; import org.springframework.web.context.request.RequestAttributes; diff --git a/src/main/java/com/rabbitmqprac/infra/oauth/client/GoogleOidcClient.java b/src/main/java/com/rabbitmqprac/infra/oauth/client/GoogleOidcClient.java index e5e9597..48d86d7 100644 --- a/src/main/java/com/rabbitmqprac/infra/oauth/client/GoogleOidcClient.java +++ b/src/main/java/com/rabbitmqprac/infra/oauth/client/GoogleOidcClient.java @@ -4,6 +4,7 @@ import com.rabbitmqprac.infra.oauth.dto.OidcPublicKeyRes; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; @@ -20,6 +21,7 @@ public class GoogleOidcClient implements OauthOidcClient { private String tokenUri; @Override + @Cacheable(value = "GoogleOauthOidc", cacheManager = "oidcCacheManager") public OidcPublicKeyRes getOidcPublicKey() { return webClient.get() .uri(jwksUri) diff --git a/src/main/java/com/rabbitmqprac/infra/oauth/client/KakaoOidcClient.java b/src/main/java/com/rabbitmqprac/infra/oauth/client/KakaoOidcClient.java index 45b5bc1..fd57063 100644 --- a/src/main/java/com/rabbitmqprac/infra/oauth/client/KakaoOidcClient.java +++ b/src/main/java/com/rabbitmqprac/infra/oauth/client/KakaoOidcClient.java @@ -4,6 +4,7 @@ import com.rabbitmqprac.infra.oauth.dto.OidcPublicKeyRes; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; @@ -21,6 +22,7 @@ public class KakaoOidcClient implements OauthOidcClient { private String tokenUri; @Override + @Cacheable(value = "KakaoOauthOidc", cacheManager = "oidcCacheManager") public OidcPublicKeyRes getOidcPublicKey() { return webClient.get() .uri(jwksUri)