【问题标题】:Why does anonymous user get redirected to expiredsessionurl by Spring Security为什么匿名用户会被 Spring Security 重定向到 expiredsessionurl
【发布时间】:2015-12-22 08:47:17
【问题描述】:

我真的很想了解 Spring Security 的工作原理,但我现在有点迷茫。这是一个简单的场景:

  1. 用户访问网站首页但未登录
  2. SecurityContextPersistenceFilter 记录没有可用的 SecurityContext 并将创建一个新的
  3. AnonymousAuthenticationFilter 使用匿名令牌填充 SecurityContextHolder
  4. 创建了一个 ID = C2A35ED5A41E29865FF53162B0024D52 的会话
  5. 用户让页面处于空闲状态,直到会话超时
  6. 用户点击“关于”页面(或再次点击主页)
  7. SecurityContextPersistenceFilter 再次记录没有可用的 SecurityContext 并且将创建一个新的
  8. AnonymousAuthenticationFilter 再次使用匿名令牌填充 SecurityContextHolder
  9. SessionManagementFilter 请求会话 ID C2A35ED5A41E29865FF53162B0024D52 的日志无效
  10. SessionManagementFilter 记录它正在启动一个新会话并重定向到 /invalidsession

这些页面配置为.authorizeRequests().antMatchers("/","/home","/about").permitAll(). 我打开了无效会话选项来处理经过身份验证的用户:.sessionManagement().invalidSessionUrl("/errors/invalidSession")。如果我注释掉该选项,那么上述所有内容都完全相同,除了步骤#10 - SessionManagementFilter 看到请求的会话 ID 无效(#9)但不会启动新会话并执行重定向(#10 )。

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

编辑

我对匿名用户和注册(经过身份验证的)用户进行了一系列测试。如果启用.sessionManagement().invalidSessionUrl("/errors/invalidSession"),那么这两种类型的用户最终都会到达错误页面。未选中 RememberMe 的经过身份验证的用户与匿名用户相同。如果选中了 RememberMe,则在 RememberMe 超时后会出现错误页面。

如果我禁用无效会话选项,用户将永远不会看到错误页面(这是有道理的)。这两种类型的用户都可以根据需要浏览公共页面,并且在会话或 RememberMe 过期后,经过身份验证的用户将被要求登录。

如果您有兴趣,这里涉及的代码在SessionManagementFilter

if (invalidSessionStrategy != null) {
    invalidSessionStrategy
        .onInvalidSessionDetected(request, response);
    return;
}

如果启用.sessionManagement().invalidSessionUrl,则调用默认方法SimpleRedirectInvalidSessionStrategy,它会执行这段代码:

if (createNewSession) {
    request.getSession();
}
redirectStrategy.sendRedirect(request, response, destinationUrl);

createNewSession布尔值可以通过setCreateNewSession(boolean createNewSession)来设置,描述为:

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

所以,在我看来,.sessionManagement().invalidSessionUrl 最适合所有页面都经过身份验证的网站。我正在查看的选项是放置在SessionManagementFilter 之前的自定义过滤器,它检查页面访问并根据需要打开/关闭“createNewSession”,或者关闭无效会话选项并在其他地方处理经过身份验证的页面(?)。我还在这个 SO 问题中偶然发现了<%@ page session=“false” %> - Why set a JSP page session = “false” directive? - 我将进一步研究。作为 Spring Security 的新手,我对正确处理这种情况的最佳实践没有很好的认识。任何帮助将不胜感激。

【问题讨论】:

    标签: spring-security


    【解决方案1】:

    好的,所以过去几周我一直在研究 Spring Security,试图了解它是如何组合在一起的。我仍在学习,但对于这种特殊情况,我发现了两种可行的方法。

    很明显的一个是绕过公共页面的安全性,如下所示:

    @Override
    public void configure(WebSecurity web) throws Exception
    {
        web
        .ignoring()
            .antMatchers("/", "/home", "/about", "/login**", "/thankyou", "/user/signup**", "/resources/**")
        ;
    }
    

    总的来说,我对网络安全的了解还不够,无法知道这是否是一种可接受的方法,但它允许匿名用户浏览网站而不会收到无效会话错误。

    更难的解决方案(对于像我这样的 Java 和 Spring 菜鸟)基于这些 SO 问题:

    Spring security invalid session redirect

    How to set a custom invalid session strategy in Spring Security

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

    authCookie.setCookie(request, response, "anonymousUser");
    or
    authCookie.setCookie(request, response, authentication.getName());
    

    我目前仅出于测试目的使用实际登录 - 它最终将只是某种简单的是/否指示符。 CustomLogoutSuccessHandler 将其重置为匿名用户

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

    @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;
        }
    }
    

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

    【讨论】:

    • 我没有得到任何 cmets,所以我将接受这个答案。自从我发布答案以来,我没有遇到任何问题,但我警告说这还没有经过生产测试。
    猜你喜欢
    • 2021-10-27
    • 2014-04-16
    • 2016-04-15
    • 2020-08-19
    • 1970-01-01
    • 2013-05-20
    • 2016-06-15
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多