Spring Security - 当 BFF 会话自然过期时,如何从 Redis 删除会话,并执行 Auth0 注销

问题描述 投票:0回答:1

我的 Spring BFF 几乎可以工作了。

显式注销

当我明确注销时,将调用以下函数,因此会话将从以下位置删除:(i) 命名空间 > 会话,(ii) 命名空间 > 会话 > 过期,(iii) 命名空间 > 会话 > 会话 id > idx,(iv)和命名空间 > 会话 > 过期(排序集)

https://github.com/dreamstar-enterprises/docs/blob/master/Spring%20BFF/BFF/src/main/kotlin/com/frontiers/bff/auth/handlers/SessionServerLogoutHandler.kt#L40

这叫这个和这个:

https://github.com/dreamstar-enterprises/docs/blob/master/Spring%20BFF/BFF/src/main/kotlin/com/frontiers/bff/auth/handlers/SessionServerLogoutHandler.kt#L40

https://github.com/dreamstar-enterprises/docs/blob/master/Spring%20BFF/BFF/src/main/kotlin/com/frontiers/bff/auth/sessions/SessionControl.kt#L53

这叫这个和这个:

https://github.com/spring-projects/spring-session/blob/main/spring-session-core/src/main/java/org/springframework/session/web/server/session/SpringSessionWebSessionStore.java# L162

https://github.com/spring-projects/spring-session/blob/main/spring-session-core/src/main/java/org/springframework/session/web/server/session/SpringSessionWebSessionStore.java# L100

两者最终都称之为:

https://github.com/spring-projects/spring-session/blob/main/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisIndexedSessionRepository.java# L387

这里调用了4个删除方法

https://github.com/spring-projects/spring-session/blob/main/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisIndexedSessionRepository.java# L391

还调用以下命令来执行 RP 发起的注销(也结束与 Auth0 授权服务器存在的会话)

  • 删除BFF会话,删除2个cookie:

https://github.com/dreamstar-enterprises/docs/blob/master/Spring%20BFF/BFF/src/main/kotlin/com/frontiers/bff/auth/handlers/SessionServerLogoutHandler.kt

  • 此处从身份验证服务器注销(RP 发起注销):

https://github.com/dreamstar-enterprises/docs/blob/master/Spring%20BFF/BFF/src/main/kotlin/com/frontiers/bff/auth/handlers/oauth2/OAuth2ServerLogoutSuccessHandler.kt

BFF 会话自然过期

但是,当 BFF 会话达到其自然到期时间时,我该如何执行上述操作。 当这种情况发生时Redis仍然留下以下内容

enter image description here

此外,Auth0 会话永远不会注销(因此,如果该人通过 Spring BFF 再次登录,并且 Auth0 会话仍然有效,并且它将静默登录而不显示 Auth0 登录页面)

spring-security redis auth0 spring-session
1个回答
0
投票

我认为Spring Session在会话过期时不会自动通知应用程序。因此,我们需要一种方法来检测 Redis 中的会话何时过期。我们可以通过设置一个计划任务来实现这一点,该任务定期检查过期的会话并相应地处理它们,类似于 SessionEvicter 清理会话的方式。一旦检测到会话过期,我们就可以

  • 从 Redis 中删除会话数据,包括您列出的所有相关键(命名空间 > 会话、命名空间 > 会话 > 过期等)。
  • 从 Auth0 中注销用户。由于会话过期不会自动与 Auth0 通信,因此我们需要发起 RP-Initiated Logout 来使 Auth0 会话失效。

此外,Spring Session 没有内置支持在会话自然过期时执行自定义逻辑,但我们可以通过订阅 Redis 键过期事件来监听会话删除。我们可以配置 Redis 来发布密钥过期事件,并在 Spring 应用程序中设置监听器来处理这些事件。

示例代码

@Configuration
public class RedisKeyExpirationListenerConfig {

    @Bean
    RedisMessageListenerContainer keyExpirationListenerContainer(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }

    @Bean
    KeyExpirationEventMessageListener redisKeyExpirationListener(
            RedisMessageListenerContainer listenerContainer) {
        return new KeyExpirationEventMessageListener(listenerContainer);
    }
}

@Component
public class KeyExpirationEventMessageListener extends KeyspaceEventMessageListener {

    public KeyExpirationEventMessageListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = message.toString();
        // Check if the key matches session keys
        if (expiredKey.startsWith("spring:session:")) {
            // Perform custom cleanup and logout from Auth0
            handleSessionExpiration(expiredKey);
        }
    }

    private void handleSessionExpiration(String sessionId) {
        // 1. Remove session-related data from Redis
        // 2. Trigger RP-Initiated Logout from Auth0
    }
}

最后注销 Auth0 会话,在处理过期会话时向 Auth0 注销端点发送请求

private fun performAuth0Logout(sessionId: String) {
    val logoutUrl = "https://YOUR_AUTH0_DOMAIN/v2/logout"
    val request = WebClient.create(logoutUrl)
        .get()
        .uri { uriBuilder ->
            uriBuilder.queryParam("client_id", "YOUR_CLIENT_ID")
                .queryParam("returnTo", "YOUR_LOGOUT_REDIRECT_URI")
                .build()
        }
        .retrieve()
        .bodyToMono(Void::class.java)
    request.subscribe(
        { logger.info("Auth0 session for session $sessionId successfully logged out.") },
        { error -> logger.error("Error logging out from Auth0: ${error.message}") }
    )
}
© www.soinside.com 2019 - 2024. All rights reserved.