当用户注销时,Spring Security 从数据库中删除会话

Posted

技术标签:

【中文标题】当用户注销时,Spring Security 从数据库中删除会话【英文标题】:Spring Security deletes session form the database when user logs out 【发布时间】:2020-11-16 03:56:20 【问题描述】:

我正在我的项目中实现 Spring Security,并使用 mysql 数据库来存储会话。一切正常,但是当用户注销时,它的会话也会从我不想要的数据库中删除。我只希望会话无效但不从数据库中删除。 在调试时,我发现:

    public void logout(HttpServletRequest request, HttpServletResponse response,
        Authentication authentication) 
    Assert.notNull(request, "HttpServletRequest required");
    if (invalidateHttpSession) 
        HttpSession session = request.getSession(false);
        if (session != null) 
            logger.debug("Invalidating session: " + session.getId());
            **session.invalidate();**
        
    

    if (clearAuthentication) 
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(null);
    

    SecurityContextHolder.clearContext();

此代码来自 SecurityContextLogoutHandler 类。

进一步,代码执行进入:

    private final class HttpSessionWrapper extends HttpSessionAdapter<S> 

        HttpSessionWrapper(S session, ServletContext servletContext) 
            super(session, servletContext);
        

        @Override
        public void invalidate() 
            super.invalidate();
            SessionRepositoryRequestWrapper.this.requestedSessionInvalidated = true;
            setCurrentSession(null);
            clearRequestedSessionCache();
            **SessionRepositoryFilter.this.sessionRepository.deleteById(getId());**
        

    

函数的最后一行删除了我不想要的会话。

我的问题是,当用户注销时,我是否可以阻止 spring security 从数据库中删除会话,或者这就是 spring security 的工作方式?

【问题讨论】:

您不应该这样做,因为删除会话会使它失效。为什么要将会话数据保留在数据库中?因此,您可能还有另一个要完成的用例,这是您应该提出的实际问题。 【参考方案1】:

一旦用户注销,您是否有任何特定原因不想从数据库中删除会话?这是非常常见的行为。会话代表您登录的客户端。一旦客户端登录(提供有效的凭据,例如带有用户名的密码)会话 ID 就会被创建并发送给客户端。此会话 ID 表示有效的登录连接。在来自该客户端的后续请求中,他只会在标头内发送此会话 ID,您的应用程序将检查此会话 ID 是否存储在有效会话(例如您的数据库)中,并且是否认为此请求已通过身份验证(因此客户端不必须发送他的凭据,每个请求都必须对其进行验证,他只发送会话 ID)。一旦客户端注销,会话 ID 就会失效,因为注销后他的连接不再经过身份验证。因此,是的,这就是 Spring Security 的工作方式,无需保留无效的会话。您还必须实施自定义机制以从数据库中清除会话(如果不是在用户注销时清除会话)。您也可以考虑在内存中使用会话池而不是 DB。

编辑:我不知道在数据库会话池的情况下弹簧检查的有效会话,但有时它必须访问数据库读取所有会话,以便它可以找出哪些会话 ID 是有效的(我想这是为每个after - 至少登录请求)。当有效会话由该池同时定义时,如何在您的情况下将无效会话保留在数据库会话池中?

【讨论】:

我不想从数据库中删除会话的原因是因为我正在保存带有一些属性的会话,并且基于这些属性我想在用户下次登录时向他/她显示一些相关信息时间就像购物网站上发生的事情一样。不过我不知道这些网站是如何做到这一点的。 另外,我同意你的观点,我必须实施自定义机制以从数据库中清除会话,但我可以稍后实施。我的动机是实现购物网站的功能(显示建议/相关信息) 此类信息不应与会话一起存储。如果您将用户保留在数据库中,为什么不将这些附加信息也保留到具有相关用户 ID 的数据库中呢?下次用户希望查看此类属性时,只需通过当前登录的用户 ID 获取这些信息的数据库。您可以简单地实现这一点,并且是执行此类操作的常用方法。不需要登录来保存连接的附加信息的网站通常使用 ip:port 组合来表示连接,但是一旦此连接失效,这些信息就会丢失。

以上是关于当用户注销时,Spring Security 从数据库中删除会话的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Security 无状态(jwt 令牌)中注销用户?

java spring boot / spring security(HttpSecurity)中的会话到期时如何自动注销

使用 Spring Security 更新角色时如何注销用户

Spring Security 自定义注销空指针异常

spring security - 如何让另一个用户注销

Spring Security 注销和最大会话数