我有一个 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 中。
Redis 中的会话之后
在我调用注销处理程序之后,肯定发生了一些事情,但会话及其密钥仍然存在,除了单个最后访问的映射键/值之外,没有值映射。
此外,SortedSet 中不会删除任何内容,根据上面的 deleteAndReturn 方法中的第四步,它应该...
那么,有人可以帮助我理解我的代码哪里可能出了问题吗?
让我们看看
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()
。