当我运行这个时:
@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);
}
他们的课程可以在这里找到:
我在 Redis 中看到的是:
然后运行注销处理程序后:
我是否打算运行另一个函数?这真的是预期的行为吗 - 将密钥留在 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 配置。看来 Spring 可能无法访问 Redis 端点/配置(不过,我在这里可能是错的),所以如果我处于你的位置并且如果标准删除不能可靠地工作,我会直接使用 RedisOperations 或RedisTemplate 手动删除会话密钥。这种方法使我能够进行更多控制,并可以帮助我绕过 ReactiveRedisIndexedSessionRepository 的潜在问题。
redisOperations.delete(sessionKey);
顺便说一句,如果removeSession没有按预期工作(由于Redis操作的根本问题,正如您所提到的),会话密钥可能会保留,导致不一致,所以您不应该将该会话清理逻辑放在finally块中确保它以任何方式执行