在第一次请求中,Spring安全'记住我'的cookie不可用

fra*_*ern 5 java cookies spring spring-security

我无法在登录请求后检索Spring记住我的cookie,但它在下一个对受保护页面的请求中工作正常.谁能告诉我如何立即掌握它?

我在登录请求中设置了记住我的cookie,但是在Spring重定向回原始(受保护的)URL之后无法对其进行检索.

一步步:

  1. 浏览器转到example.com/protected
  2. Spring重定向到登录表单页面
  3. 成功登录后,SPRING_SECURITY_REMEMBER_ME_COOKIE设置在一个非常薄的自定义子类org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices中
  4. 看起来Spring重定向回example.com/protected,没有往返浏览器,并且登录"servlet"和受保护页面都由Tomcat 6中的同一个线程处理.
  5. 我们的org.springframework.security.web.access.expression.WebSecurityExpressionRoot的子类具有从<intercept-url pattern ="..."access ="method()"/>调用的方法
  6. 在我们的method()中,request.getCookies()不会在第一个请求中给出记住我的cookie,而是在之后的所有请求中.
  7. 我们的应用程序存在一些问题,因为cookie丢失了......

到目前为止,我的理论是我不理解SavedRequest.

压缩配置在这里:

<http auto-config="false" use-expressions="true" authentication-manager-ref="myAuthenticationManager" path-type="regex">
    <form-login authentication-success-handler-ref="myAuthenticationSuccessHandler" login-page="..." login-processing-url="..." authentication-failure-url="..." username-parameter="username" password-parameter="password" />

    <custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
    <expression-handler ref="myWebSecurityExpressionHandler" />

    <custom-filter ref="myCustomeFilter1" before="FORM_LOGIN_FILTER"/>
    <custom-filter ref="myCustomeFilter2" position="BASIC_AUTH_FILTER"/>
    <custom-filter ref="mySecurityClientTokenAuthenticationFilter" after="LOGOUT_FILTER" />

    <access-denied-handler ref="myAccessDeniedHandler"/>
    <intercept-url pattern="xxx"
                   access="method()"/>

    <intercept-url pattern="yyy"
                   access="method()"/>
    <remember-me services-ref="rememberMeServices"  key="my_remember"/>
</http>
Run Code Online (Sandbox Code Playgroud)

我尝试添加以下内容,唯一的结果是用户没有被重定向到原始页面.

<http ...
    <request-cache ref="nullRequestCache"/>
</http>
<bean:bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>
Run Code Online (Sandbox Code Playgroud)

lut*_*ute 1

RememberMeService的autoLogin()方法中使用request.getCookie()时,传入的请求是SavedRequestAwareWrapper,它封装了原始请求和保存的请求,并重写了getCookies方法。

@Override
public Cookie[] getCookies() {
    List<Cookie> cookies = savedRequest.getCookies();
    return cookies.toArray(new Cookie[cookies.size()]);
}
Run Code Online (Sandbox Code Playgroud)

因此,当你想从请求中获取cookie时,你实际上是从savedRequest中获取cookie。然而,cookie可能存在于原始请求中。

您可能应该收到获取所需 cookie 的原始请求。例如:

public class ApplicationRememberMeServiceImpl implements RememberMeService, LogoutHandler {

    public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {

        HttpServletRequestWrapper savedRequestWrapper = (HttpServletRequestWrapper) ((HttpServletRequestWrapper) request).getRequest();
        HttpServletRequest httpServletRequest = (HttpServletRequest) savedRequestWrapper.getRequest();

        Cookie cookie = WebUtils.getCookie(httpServletRequest, cookieName);
        // logic continues...

    }

}
Run Code Online (Sandbox Code Playgroud)

更新 03/05/2015

因为spring security会对原始的HttpServletRequest进行多次包装,所以通过下面的方式提取原始请求会更加安全:

public class ApplicationRememberMeServiceImpl implements RememberMeService, LogoutHandler {

    public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {

        HttpServletRequest httpServletRequest = request;

        // Get the original request from multiple wrapped HttpServletRequest
        if(httpServletRequest instanceof HttpServletRequestWrapper) {

            HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) httpServletRequest;
            while(httpServletRequestWrapper.getRequest() instanceof HttpServletRequestWrapper) {
                httpServletRequestWrapper = (HttpServletRequestWrapper) httpServletRequestWrapper.getRequest();
            }

            httpServletRequest = (HttpServletRequest) httpServletRequestWrapper.getRequest();

        }

        Cookie cookie = WebUtils.getCookie(httpServletRequest, cookieName);
        // logic continues...

    }

}
Run Code Online (Sandbox Code Playgroud)