Spring security 为什么Spring Security会将匿名用户重定向到expiredsessionurl

Spring security 为什么Spring Security会将匿名用户重定向到expiredsessionurl,spring-security,Spring Security,我真的很想了解SpringSecurity是如何工作的,但我现在有点不知所措。下面是一个简单的场景: 用户访问网站主页但不登录 SecurityContextPersistenceFilter记录没有可用的SecurityContext,并将创建一个新的SecurityContext AnonymousAuthenticationFilter使用匿名令牌填充SecurityContextHolder 创建ID为C2A35ED5A41E29865FF53162B0024D52的会话 用户让页面处于

我真的很想了解SpringSecurity是如何工作的,但我现在有点不知所措。下面是一个简单的场景:

  • 用户访问网站主页但不登录
  • SecurityContextPersistenceFilter
    记录没有可用的SecurityContext,并将创建一个新的SecurityContext
  • AnonymousAuthenticationFilter
    使用匿名令牌填充SecurityContextHolder
  • 创建ID为C2A35ED5A41E29865FF53162B0024D52的会话
  • 用户让页面处于空闲状态,直到会话超时
  • 用户单击关于页面(或再次单击主页)
  • SecurityContextPersistenceFilter
    再次记录没有可用的SecurityContext,并将创建一个新的SecurityContext
  • AnonymousAuthenticationFilter
    再次使用匿名令牌填充SecurityContextHolder
  • SessionManagementFilter
    请求会话ID C2A35ED5A41E29865FF53162B0024D52的日志无效
  • SessionManagementFilter
    记录它正在启动新会话并重定向到/invalidsession
  • 这些页面被配置为
    .authorizeRequests().antMatchers(“/”、“/home”、“/about”).permitAll()。
    我打开了无效会话选项来处理经过身份验证的用户:
    .sessionManagement().invalidSessionUrl(“/errors/invalidSession”)
    。如果我注释掉该选项,那么上面描述的所有内容都完全相同,除了步骤#10-
    SessionManagementFilter
    发现请求的会话ID无效(#9),但不会启动新会话并执行重定向(#10)

    为什么??如何保持无效会话选项,但正确处理匿名用户,即不重定向?或者这是不可能的,我必须单独处理经过身份验证的用户?如果有人能帮助我了解这里发生的事情并为我指明解决问题的方向,我将非常感激。如果需要查看我的完整http配置,请告诉我

    编辑

    我对匿名和注册(认证)用户进行了一系列测试。如果启用了
    .sessionManagement().invalidSessionUrl(“/errors/invalidSession”)
    ,则这两种类型的用户最终都将到达错误页面。未选中Memberme的已验证用户与其他用户相同。如果选中了RememberMe,则RememberMe超时后将显示错误页面

    如果禁用无效会话选项,则没有用户会获得错误页面(这是有意义的)。这两种类型的用户都可以随意浏览公共页面,经过身份验证的用户将在会话或Memberme过期后被要求登录

    如果您感兴趣,这里涉及的代码位于
    SessionManagementFilter

    if (invalidSessionStrategy != null) {
        invalidSessionStrategy
            .onInvalidSessionDetected(request, response);
        return;
    }
    
    如果启用了
    .sessionManagement().invalidSessionUrl
    ,则调用默认方法
    SimpleDirectInvalidSessionStrategy
    ,该方法执行以下代码:

    if (createNewSession) {
        request.getSession();
    }
    redirectStrategy.sendRedirect(request, response, destinationUrl);
    
    可以通过
    setCreateNewSession(布尔createNewSession)
    设置
    createNewSession
    布尔值,其描述如下:

    确定是否应在重定向之前创建新会话(以避免在重定向请求中发送相同会话ID时可能出现的循环问题)。或者,确保配置的URL不会通过
    会话管理筛选器


    因此,在我看来,
    .sessionManagement().invalidSessionUrl
    最适合所有页面都经过身份验证的站点。我正在查看的选项是放置在
    SessionManagementFilter
    之前的自定义筛选器,它检查页面访问并根据需要打开/关闭“createNewSession”,或者关闭无效会话选项并在其他地方处理已验证页面(?)。在这个问题中,我还偶然发现了
    ,我将进一步探讨这个问题。由于对Spring Security如此陌生,我对正确处理这种情况的最佳实践没有很好的认识。任何帮助都将不胜感激。

    好的,所以我花了过去几周的时间在Spring Security上四处寻找,试图了解这一切是如何结合在一起的。我还在学习,但对于这种特殊情况,我发现有两种方法是有效的

    显而易见的一点是绕过公共页面的安全性,如下所示:

    @Override
    public void configure(WebSecurity web) throws Exception
    {
        web
        .ignoring()
            .antMatchers("/", "/home", "/about", "/login**", "/thankyou", "/user/signup**", "/resources/**")
        ;
    }
    
    @Override
    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) 
        throws IOException, ServletException {
    
        String url = destinationUrl;
    
        //reset context default value
        redirectStrategy.setContextRelative(false);
    
        if (authCookie.isCurrentCookieAnonymous()) {
            //pass the URL originally requested by the anonymous user
            url = request.getRequestURI();
            //the URL needs to have the context removed
            redirectStrategy.setContextRelative(true);
        }
    
        //always revert to anonymous user
        authCookie.setCookie(request, response, "anonymousUser");
    
        logger.debug("Starting new session (if required) and redirecting to '" + url + "'");
    
        if (createNewSession)
            request.getSession();
    
        redirectStrategy.sendRedirect(request, response, url);
    }
    
    一般来说,我对web安全性的了解还不够,不知道这是不是一种可以接受的方法,但它允许匿名用户浏览站点,而不会出现无效的会话错误

    更难的解决方案(对于像我这样的Java和Spring noob)是基于以下问题:

    默认的
    SimpleDirectInvalidSessionStrategy
    类是
    final
    ,这意味着我必须创建该类的基本副本(不确定这是一个多么好的想法)。您不能使用会话属性,因为会话在到达此策略时已被销毁,所以我为名为authUser的会话cookie创建了一个helper类(如果有人想查看,我可以发布该类)。cookie是在
    loginsucesshandler
    RememberMeSuccessHandler
    中创建或更新的,它指示用户是匿名的还是经过身份验证的:

    authCookie.setCookie(request, response, "anonymousUser");
    or
    authCookie.setCookie(request, response, authentication.getName());
    
    我目前使用的实际登录仅用于测试目的——它最终将只是某种简单的是/否指示符<代码>CustomLogoutSuccessHandler将其重置为匿名用户

    无效的会话方法如下所示:

    @Override
    public void configure(WebSecurity web) throws Exception
    {
        web
        .ignoring()
            .antMatchers("/", "/home", "/about", "/login**", "/thankyou", "/user/signup**", "/resources/**")
        ;
    }
    
    @Override
    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) 
        throws IOException, ServletException {
    
        String url = destinationUrl;
    
        //reset context default value
        redirectStrategy.setContextRelative(false);
    
        if (authCookie.isCurrentCookieAnonymous()) {
            //pass the URL originally requested by the anonymous user
            url = request.getRequestURI();
            //the URL needs to have the context removed
            redirectStrategy.setContextRelative(true);
        }
    
        //always revert to anonymous user
        authCookie.setCookie(request, response, "anonymousUser");
    
        logger.debug("Starting new session (if required) and redirecting to '" + url + "'");
    
        if (createNewSession)
            request.getSession();
    
        redirectStrategy.sendRedirect(request, response, url);
    }
    
    同样,如果需要,我可以发布完整的课程

    SecurityConfig
    类包括以下内容:

    @Bean
    public SessionManagementBeanPostProcessor sessionManagementBeanPostProcessor() {
        return new SessionManagementBeanPostProcessor();
    }
    
    protected static class SessionManagementBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            if (bean instanceof SessionManagementFilter) {
                SessionManagementFilter filter = (SessionManagementFilter) bean;
                filter.setInvalidSessionStrategy(new RedirectInvalidSession("/errors/invalidSession"));
            }
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            return bean;
        }
    }
    

    到目前为止,我的测试对于匿名用户和经过身份验证的用户都是成功的,但是这种方法还没有经过生产测试。

    我没有收到任何这样或那样的评论,所以我将接受这个答案。