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

时间:2015-09-24 16:35:28

标签: spring-security

我真的想了解Spring Security是如何运作的,但我此刻有点失落。这是一个简单的场景:

  1. 用户访问网站主页但未登录
  2. SecurityContextPersistenceFilter记录没有可用的SecurityContext,并且将创建一个新的
  3. AnonymousAuthenticationFilter使用匿名令牌填充SecurityContextHolder
  4. 使用ID = C2A35ED5A41E29865FF53162B0024D52
  5. 创建会话
  6. 用户可让页面闲置,直到会话超时
  7. 用户点击“关于”页面(或主页)
  8. SecurityContextPersistenceFilter再次记录没有可用的SecurityContext,并且将创建一个新的
  9. AnonymousAuthenticationFilter再次使用匿名令牌填充SecurityContextHolder
  10. 请求会话ID为C2A35ED5A41E29865FF53162B0024D52的
  11. SessionManagementFilter日志无效
  12. SessionManagementFilter记录它正在开始新会话并重定向到/ invalidsession
  13. 这些页面配置为.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之前的自定义过滤器,用于检查页面访问权限并转换“创建新事件”。根据需要打开/关闭或关闭无效会话选项并在其他地方处理经过身份验证的页面(?)。在这个问题中我也偶然发现了<%@ page session=“false” %> - Why set a JSP page session = “false” directive? - 我将进一步研究这个问题。对Spring Security如此陌生,我不能很好地理解正确处理这种情况的最佳实践。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:2)

好的,所以我花了几周的时间在Spring Security中挖掘,试图了解它们是如何组合在一起的。我还在学习,但在这种特殊情况下,我找到了两种方法。

显而易见的就是绕过这样的公共页面的安全性:

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

我仍然不了解网络安全性,知道这是否是一种可接受的方法,但它允许匿名用户浏览网站,但没有得到无效的会话错误。

更难的解决方案(对于像我这样的Java和Spring noob)基于这些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将其重置为anonymousUser

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

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

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