From e7dd69de0006240ea287d8a2246c6b32e69da4ac Mon Sep 17 00:00:00 2001 From: Dreamstar Enterprises <39380005+dreamstar-enterprises@users.noreply.github.com> Date: Sun, 11 Aug 2024 23:11:19 +0100 Subject: [PATCH] Add files via upload --- .../auth/sessions/CustomSessionIdGenerator.kt | 23 ++++++ .../sessions/RedisSessionCleanUpConfig.kt | 10 ++- .../auth/sessions/RedisSessionMapperConfig.kt | 71 +++++++++++++++++++ .../sessions/SessionApplicationListener.kt | 21 ++++++ .../auth/sessions/SessionListenerConfig.kt | 51 +++++++++++++ .../bff/auth/sessions/SessionService.kt | 58 +++++++++++++++ .../auth/sessions/WebSessionStoreConfig.kt | 5 +- 7 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 Spring BFF/bff/auth/sessions/CustomSessionIdGenerator.kt create mode 100644 Spring BFF/bff/auth/sessions/RedisSessionMapperConfig.kt create mode 100644 Spring BFF/bff/auth/sessions/SessionApplicationListener.kt create mode 100644 Spring BFF/bff/auth/sessions/SessionListenerConfig.kt create mode 100644 Spring BFF/bff/auth/sessions/SessionService.kt diff --git a/Spring BFF/bff/auth/sessions/CustomSessionIdGenerator.kt b/Spring BFF/bff/auth/sessions/CustomSessionIdGenerator.kt new file mode 100644 index 0000000..63987a6 --- /dev/null +++ b/Spring BFF/bff/auth/sessions/CustomSessionIdGenerator.kt @@ -0,0 +1,23 @@ +package com.example.bff.auth.sessions + +import org.springframework.session.SessionIdGenerator + +/**********************************************************************************************************************/ +/**************************************************** SESSION ID GENERATOR ********************************************/ +/**********************************************************************************************************************/ + +// more here: +// https://docs.spring.io/spring-session/reference/configuration/common.html#changing-how-session-ids-are-generated + +internal class CustomSessionIdGenerator : SessionIdGenerator { + + override fun generate(): String { + // use a UUID with a custom prefix or other unique generation logic + return "BFF-${java.util.UUID.randomUUID()}" + } + +} + +/**********************************************************************************************************************/ +/**************************************************** END OF KOTLIN ***************************************************/ +/**********************************************************************************************************************/ \ No newline at end of file diff --git a/Spring BFF/bff/auth/sessions/RedisSessionCleanUpConfig.kt b/Spring BFF/bff/auth/sessions/RedisSessionCleanUpConfig.kt index 75df8ee..fe01e98 100644 --- a/Spring BFF/bff/auth/sessions/RedisSessionCleanUpConfig.kt +++ b/Spring BFF/bff/auth/sessions/RedisSessionCleanUpConfig.kt @@ -1,5 +1,7 @@ package com.example.bff.auth.redis +import com.example.bff.props.SpringDataProperties +import com.example.bff.props.SpringSessionProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.domain.Range @@ -19,10 +21,12 @@ import java.time.Instant /**********************************************************************************************************************/ // more here: -// https://docs.spring.io/spring-session/reference/configuration/reactive-redis-indexed.html +// https://docs.spring.io/spring-session/reference/configuration/reactive-redis-indexed.html#how-spring-session-cleans-up-expired-sessions @Configuration -internal class RedisCleanUpConfig { +internal class RedisCleanUpConfig ( + private val springSessionProperties: SpringSessionProperties, +){ @Bean // configuring Redis to Send Keyspace Events @@ -35,7 +39,7 @@ internal class RedisCleanUpConfig { fun reactiveSessionRepositoryCustomizer(): ReactiveSessionRepositoryCustomizer { return ReactiveSessionRepositoryCustomizer { sessionRepository: ReactiveRedisIndexedSessionRepository -> sessionRepository.setCleanupInterval( - Duration.ofSeconds(30) + springSessionProperties.redis?.sessionCleanUpFrequency?.let { Duration.ofSeconds(it) } ) } } diff --git a/Spring BFF/bff/auth/sessions/RedisSessionMapperConfig.kt b/Spring BFF/bff/auth/sessions/RedisSessionMapperConfig.kt new file mode 100644 index 0000000..a16af25 --- /dev/null +++ b/Spring BFF/bff/auth/sessions/RedisSessionMapperConfig.kt @@ -0,0 +1,71 @@ +package com.example.bff.auth.sessions + +import com.example.bff.props.SpringSessionProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.core.ReactiveRedisOperations +import org.springframework.session.MapSession +import org.springframework.session.config.ReactiveSessionRepositoryCustomizer +import org.springframework.session.data.redis.ReactiveRedisIndexedSessionRepository +import org.springframework.session.data.redis.RedisSessionMapper +import reactor.core.publisher.Mono +import java.util.function.BiFunction + +/**********************************************************************************************************************/ +/************************************************ REDIS CONFIGURATION *************************************************/ +/**********************************************************************************************************************/ + +// more here: +// https://docs.spring.io/spring-session/reference/configuration/redis.html#configuring-redis-session-mapper + +@Configuration +internal class RedisSessionMapperConfig() { + + /** + * Customizes the ReactiveRedisIndexedSessionRepository to use the SafeSessionMapper. + */ + @Bean + fun redisSessionRepositoryCustomizer(): ReactiveSessionRepositoryCustomizer { + return ReactiveSessionRepositoryCustomizer { redisSessionRepository -> + redisSessionRepository.setRedisSessionMapper( + SafeRedisSessionMapper( + redisSessionRepository.sessionRedisOperations + ) + ) + } + } + + /** + * Implementation of SafeSessionMapper. + */ + internal class SafeRedisSessionMapper( + private var redisOperations: ReactiveRedisOperations + ) : BiFunction, Mono>{ + + private val redisProperties = SpringSessionProperties() + private val delegate = RedisSessionMapper() + + /** + * Custom session mapper that delegates to the default RedisSessionMapper. + * If an exception occurs, the session is deleted from Redis. + */ + override fun apply(sessionId: String, map: Map): Mono { + return Mono.defer { + try { + // attempt to apply the session mapping + Mono.just(delegate.apply(sessionId, map)) + } catch (ex: IllegalStateException) { + // handle exception: delete session from Redis and return empty Mono + redisOperations.delete("${redisProperties.redis?.namespace}:$sessionId") + .then(Mono.empty()) + } + } + } + + } + +} + +/**********************************************************************************************************************/ +/**************************************************** END OF KOTLIN ***************************************************/ +/**********************************************************************************************************************/ \ No newline at end of file diff --git a/Spring BFF/bff/auth/sessions/SessionApplicationListener.kt b/Spring BFF/bff/auth/sessions/SessionApplicationListener.kt new file mode 100644 index 0000000..d467b52 --- /dev/null +++ b/Spring BFF/bff/auth/sessions/SessionApplicationListener.kt @@ -0,0 +1,21 @@ +package com.example.bff.auth.sessions + +import com.example.bff.auth.redis.RedisConnectionFactoryConfig +import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer +import org.springframework.stereotype.Component + +/**********************************************************************************************************************/ +/************************************************ REDIS CONFIGURATION *************************************************/ +/**********************************************************************************************************************/ + +// more here: +// https://docs.spring.io/spring-session/reference/http-session.html#httpsession-redis-jc +// https://docs.spring.io/spring-session/reference/http-session.html#_servlet_container_initialization_2 + +@Component +internal class SessionApplicationInitialiser + : AbstractHttpSessionApplicationInitializer(RedisConnectionFactoryConfig::class.java) + +/**********************************************************************************************************************/ +/**************************************************** END OF KOTLIN ***************************************************/ +/**********************************************************************************************************************/ \ No newline at end of file diff --git a/Spring BFF/bff/auth/sessions/SessionListenerConfig.kt b/Spring BFF/bff/auth/sessions/SessionListenerConfig.kt new file mode 100644 index 0000000..655f5e5 --- /dev/null +++ b/Spring BFF/bff/auth/sessions/SessionListenerConfig.kt @@ -0,0 +1,51 @@ +package com.example.bff.auth.sessions + +import org.springframework.context.event.EventListener +import org.springframework.session.events.SessionCreatedEvent +import org.springframework.session.events.SessionDeletedEvent +import org.springframework.session.events.SessionExpiredEvent +import org.springframework.stereotype.Component +import reactor.core.publisher.Mono + +/**********************************************************************************************************************/ +/************************************************** SESSION CONFIGURATION *********************************************/ +/**********************************************************************************************************************/ + +// more here: +// https://docs.spring.io/spring-session/reference/configuration/reactive-redis-indexed.html#listening-session-events + +@Component +// session event listener +internal class SessionListenerConfig { + + @EventListener + fun processSessionCreatedEvent(event: SessionCreatedEvent): Mono { + return Mono.fromRunnable { + // Log or print information about the created session + println("Session created: ${event.sessionId}") + // Your logic for session created event + } + } + + @EventListener + fun processSessionDeletedEvent(event: SessionDeletedEvent): Mono { + return Mono.fromRunnable { + // Log or print information about the deleted session + println("Session deleted: ${event.sessionId}") + // Your logic for session deleted event + } + } + + @EventListener + fun processSessionExpiredEvent(event: SessionExpiredEvent): Mono { + return Mono.fromRunnable { + // Log or print information about the expired session + println("Session expired: ${event.sessionId}") + // Your logic for session expired event + } + } +} + +/**********************************************************************************************************************/ +/**************************************************** END OF KOTLIN ***************************************************/ +/**********************************************************************************************************************/ \ No newline at end of file diff --git a/Spring BFF/bff/auth/sessions/SessionService.kt b/Spring BFF/bff/auth/sessions/SessionService.kt new file mode 100644 index 0000000..2c444b7 --- /dev/null +++ b/Spring BFF/bff/auth/sessions/SessionService.kt @@ -0,0 +1,58 @@ +package com.example.bff.auth.sessions + +import org.springframework.session.FindByIndexNameSessionRepository +import org.springframework.session.ReactiveFindByIndexNameSessionRepository +import org.springframework.session.Session +import org.springframework.session.data.redis.ReactiveRedisIndexedSessionRepository +import org.springframework.session.data.redis.ReactiveRedisIndexedSessionRepository.RedisSession +import org.springframework.stereotype.Service +import reactor.core.publisher.Flux +import reactor.core.publisher.Mono +import java.security.Principal + +/**********************************************************************************************************************/ +/************************************************ SESSION CONFIGURATION ***********************************************/ +/**********************************************************************************************************************/ + +// more here: +// https://docs.spring.io/spring-session/reference/configuration/redis.html#finding-all-user-sessions + +@Service +internal class SessionService( + private val sessions: ReactiveFindByIndexNameSessionRepository, + private val redisIndexedSessionRepository: ReactiveRedisIndexedSessionRepository +) { + + /** + * Retrieves all sessions for a specific user. + * @param principal the principal whose sessions need to be retrieved + * @return a Flux of sessions for the specified user + */ + fun getSessions(principal: Principal): Flux { + return sessions.findByPrincipalName(principal.name) + .flatMapMany { sessionsMap -> + Flux.fromIterable(sessionsMap.values) + } + } + + /** + * Removes a specific session for a user. + * @param principal the principal whose session needs to be removed + * @param sessionIdToDelete the ID of the session to be removed + * @return a Mono indicating completion or error + */ + fun removeSession(principal: Principal, sessionIdToDelete: String): Mono { + return sessions.findByPrincipalName(principal.name) + .flatMap { userSessions -> + if (userSessions.containsKey(sessionIdToDelete)) { + redisIndexedSessionRepository.deleteById(sessionIdToDelete) + } else { + Mono.empty() + } + } + } +} + +/**********************************************************************************************************************/ +/**************************************************** END OF KOTLIN ***************************************************/ +/**********************************************************************************************************************/ \ No newline at end of file diff --git a/Spring BFF/bff/auth/sessions/WebSessionStoreConfig.kt b/Spring BFF/bff/auth/sessions/WebSessionStoreConfig.kt index e1d1310..be1fc70 100644 --- a/Spring BFF/bff/auth/sessions/WebSessionStoreConfig.kt +++ b/Spring BFF/bff/auth/sessions/WebSessionStoreConfig.kt @@ -3,6 +3,7 @@ package com.example.bff.auth.sessions import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.session.data.redis.ReactiveRedisIndexedSessionRepository +import org.springframework.session.data.redis.ReactiveRedisIndexedSessionRepository.RedisSession import org.springframework.session.web.server.session.SpringSessionWebSessionStore import org.springframework.web.server.session.CookieWebSessionIdResolver import org.springframework.web.server.session.DefaultWebSessionManager @@ -36,14 +37,14 @@ internal class WebSessionStoreConfig { @Bean(name = ["webSessionStore"]) fun webSessionStore( reactiveRedisIndexedSessionRepository: ReactiveRedisIndexedSessionRepository - ): SpringSessionWebSessionStore { + ): SpringSessionWebSessionStore { return SpringSessionWebSessionStore(reactiveRedisIndexedSessionRepository) } @Bean(name = ["webSessionManager"]) fun webSessionManager( cookieWebSessionIdResolver: CookieWebSessionIdResolver, - webSessionStore: SpringSessionWebSessionStore + webSessionStore: SpringSessionWebSessionStore ): WebSessionManager { val sessionManager = DefaultWebSessionManager() sessionManager.sessionStore = webSessionStore