Spring 会话/安全 - Redis 会话未正确删除

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

我有一个 Spring OAuth 客户端(BFF),位于公共 Angular 客户端和 Auth0 授权服务器之间。当我登录时,BFF 正确地将会话保留到 Redis(并且授权客户端、安全上下文和授权请求作为会话中的属性)

当我注销时,只有会话的内容被删除,密钥本身没有被删除。而且排序集中的任何内容都不会被删除。

注销处理程序

这是我的注销处理程序

@Component
internal class SessionServerLogoutHandler(
    private val sessionControl: SessionControl,
    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
                sessionControl.invalidateSession(session)
                    .then(Mono.fromRunnable {

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

                        // delete the session cookie
                        val sessionCookie = ResponseCookie.from(sessionProperties.SESSION_COOKIE_NAME)
                        sessionCookie.maxAge(0)
                        sessionCookie.httpOnly(sessionProperties.SESSION_COOKIE_HTTP_ONLY)
                        sessionCookie.secure(sessionProperties.SESSION_COOKIE_SECURE)
                        sessionCookie.sameSite(sessionProperties.SESSION_COOKIE_SAME_SITE)
                        sessionCookie.path(sessionProperties.SESSION_COOKIE_PATH)
                        sessionCookie.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)
                        csrfCookie.maxAge(0)
                        csrfCookie.httpOnly(csrfProperties.CSRF_COOKIE_HTTP_ONLY)
                        csrfCookie.secure(csrfProperties.CSRF_COOKIE_SECURE)
                        csrfCookie.sameSite(csrfProperties.CSRF_COOKIE_SAME_SITE)
                        csrfCookie.path(csrfProperties.CSRF_COOKIE_PATH)
                        csrfCookie.domain(csrfProperties.CSRF_COOKIE_DOMAIN)
                        .build()
                        response.headers.add(
                            HttpHeaders.SET_COOKIE,
                            csrfCookie.toString()
                        )
                    })
            }
    }
}

会话控制

它调用另一个名为 SessionControl 的调用,以及 invalidate session 方法。这是这个功能

fun invalidateSession(session: WebSession): Mono<Void> {
        val sessionInformation = getSessionInformation(session)
        logger.info("Invalidating sessionId: ${sessionInformation.sessionId}")
        // handle the session invalidation process
        return sessionInformation.invalidate()
            .then(Mono.defer {
                webSessionStore.removeSession(sessionInformation.sessionId)
            })
            .doOnSuccess {
                logger.info("Session invalidated and removed: ${sessionInformation.sessionId}")
            }
            .doOnError { error ->
                logger.error("Error invalidating session: ${sessionInformation.sessionId}", error)
            }
    }

WebSessionStore

依次调用Websession Store,即removeSession方法。

这是 bean 和函数:

@Bean(name = ["webSessionStore"])
fun webSessionStore(
    reactiveRedisIndexedSessionRepository: ReactiveRedisIndexedSessionRepository
): SpringSessionWebSessionStore<RedisSession> {
    return SpringSessionWebSessionStore(reactiveRedisIndexedSessionRepository)
}

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

reactiveRedisIndexedSessionRepository

上面的this.sessions指的是WebsessionStore的构造函数中传入的ReactiveRedisIndexedSessionRepository。看看 Spring ReactiveRedisIndexedSessionRepository 的内部结构,我看到了这一点:

public Mono<Void> deleteById(String id) {
        return deleteAndReturn(id).then();
    }

    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
    }

之前Redis中的Session

正如您在我注销之前看到的,会话位于 Redis 中。

enter image description here

Redis 中的会话之后

在我调用注销处理程序之后,肯定发生了一些事情,但会话及其密钥仍然存在,除了单个最后访问的映射键/值之外,没有值映射。

enter image description here

此外,SortedSet 中不会删除任何内容,根据上面的 deleteAndReturn 方法中的第四步,它应该...

enter image description here

那么,有人可以帮助我理解我的代码哪里可能出了问题吗?

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

让我们看看

deleteAndReturn

    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
    }

您收到一个

id
,但您几乎完全忽略它。相反,您尝试通过
id
session
获取
session.getId()
,但会话已失效。相反,您需要使用并传递
id
而不是
session.getId()

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