Spring Security / Spring Session 未正确删除 Redis 会话

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

当我运行这个时:

@Component
internal class SessionServerLogoutHandler(
    private val sessionControl: SessionControl,
    private val webSessionStore: SpringSessionWebSessionStore<ReactiveRedisIndexedSessionRepository.RedisSession>,
    private val sessionProperties: SessionProperties,
    private val csrfProperties: CsrfProperties,
) : ServerLogoutHandler {

    private val logger = LoggerFactory.getLogger(SessionServerLogoutHandler::class.java)

    override fun logout(exchange: WebFilterExchange, authentication: Authentication?): Mono<Void> {
        return exchange.exchange.session
            .flatMap { session ->
                logger.info("Logging out: Invalidating User Session: ${session.id}")

                val response = exchange.exchange.response

                // invalidate and delete session
                webSessionStore.removeSession(session.id)
                    .then(Mono.fromRunnable {

                        logger.info("Deleting Session Cookie: ${sessionProperties.SESSION_COOKIE_NAME}")

                        // delete the session cookie
                        val sessionCookie = ResponseCookie.from(sessionProperties.SESSION_COOKIE_NAME)
                        .maxAge(0)
                        .httpOnly(sessionProperties.SESSION_COOKIE_HTTP_ONLY)
                        .secure(sessionProperties.SESSION_COOKIE_SECURE)
                        .sameSite(sessionProperties.SESSION_COOKIE_SAME_SITE)
                        .path(sessionProperties.SESSION_COOKIE_PATH)
                        .domain(sessionProperties.SESSION_COOKIE_DOMAIN)
                        .build()
                        response.headers.add(
                            HttpHeaders.SET_COOKIE,
                            sessionCookie.toString()
                        )

                        logger.info("Deleting Session Cookie: ${csrfProperties.CSRF_COOKIE_NAME}")

                        // delete the CSRF cookie
                        val csrfCookie = ResponseCookie.from(csrfProperties.CSRF_COOKIE_NAME)
                        .maxAge(0)
                        .httpOnly(csrfProperties.CSRF_COOKIE_HTTP_ONLY)
                        .secure(csrfProperties.CSRF_COOKIE_SECURE)
                        .sameSite(csrfProperties.CSRF_COOKIE_SAME_SITE)
                        .path(csrfProperties.CSRF_COOKIE_PATH)
                        .domain(csrfProperties.CSRF_COOKIE_DOMAIN)
                        .build()
                        response.headers.add(
                            HttpHeaders.SET_COOKIE,
                            csrfCookie.toString()
                        )
                    })
            }
    }
}

关键是:

webSessionStore.removeSession(session.id)

Spring 自己的底层实现是在 SpringSessionWebSessionStore 类中,是这样的:

@Override
public Mono<Void> removeSession(String sessionId) {
    return this.sessions.deleteById(sessionId);
}

他们的课程可以在这里找到:

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

我在 Redis 中看到的是:

enter image description here

然后运行注销处理程序后:

enter image description here

我是否打算运行另一个函数?这真的是预期的行为吗 - 将密钥留在 Redis 中?

如果我的很多代码看到一个密钥(用户会话实际存在),但只有 LastAccessedDate 属性,则会抛出序列化错误 - 例如在获取会话时检查用户是否经过身份验证,并检查安全上下文属性。

有人可以帮忙吗?我看不出我自己做错了什么?也许我需要调用其他函数?

或者直接使用RedisOperations或RedisTemplate?

而不是 this.sessions.deleteById(sessionId),这真的是

reactiveRedisIndexedSessionRepository.deleteById(sessionId)

不过,上面的方法应该有效吗?

Spring 存储库代码 ReactiveRedisIndexedSessionRepository 的这一部分对我来说不起作用。什么都没有被删除...

private Mono<RedisSession> deleteAndReturn(String id) {
        // @formatter:off
        return getSession(id, true)
                .flatMap((session) -> this.sessionRedisOperations.delete(getExpiredKey(session.getId()))
                        .thenReturn(session))
                .flatMap((session) -> this.sessionRedisOperations.delete(getSessionKey(session.getId())).thenReturn(session))
                .flatMap((session) -> this.indexer.delete(session.getId()).thenReturn(session))
                .flatMap((session) -> this.expirationStore.remove(session.getId()).thenReturn(session));
        // @formatter:on
    }
spring-security redis spring-session
1个回答
0
投票

整体代码看起来不错,但我不确定您的应用程序如何使用 Spring 配置。看来 Spring 可能无法访问 Redis 端点/配置(不过,我在这里可能是错的),所以如果我处于你的位置并且如果标准删除不能可靠地工作,我会直接使用 RedisOperations 或RedisTemplate 手动删除会话密钥。这种方法使我能够进行更多控制,并可以帮助我绕过 ReactiveRedisIndexedSessionRepository 的潜在问题。

redisOperations.delete(sessionKey);

顺便说一句,如果removeSession没有按预期工作(由于Redis操作的根本问题,正如您所提到的),会话密钥可能会保留,导致不一致,所以您不应该将该会话清理逻辑放在finally块中确保它以任何方式执行

© www.soinside.com 2019 - 2024. All rights reserved.