CSRF与Spring Security集成时,会话超时导致Spring MVC中的访问被拒绝


问题内容

我在Spring MVC项目中将CSRF令牌与Spring Security集成在一起。一切都可以与CSRF令牌一起正常使用,令牌将从客户端发送到服务器端。

我已经更改了logout流程,使其成为POST发送CSRF令牌的方法,并且工作正常。

发生会话超时时,我遇到了问题,需要将其重定向到spring默认注销URL,但是它提供了Access Denied该URL。

如何重写此行为。

我在安全性配置文件中包含以下行

   <http>
         //Other config parameters
        <csrf/>
   </http>

如果有人需要更多信息,请告诉我。


问题答案:

这个问题有点老了,但是答案总是有用的。

首先,这是会话支持的CSRF令牌的一个已知问题,如docs:CSRF Caveats-
Timeouts中所述

要解决此问题,请使用一些Javascript来检测即将到来的超时,使用与会话无关的CSRF令牌存储库或创建自定义AccessDeniedHandler路由。我选择了后者:

配置XML:

<http>
    <!-- ... -->
    <access-denied-handler ref="myAccessDeniedHandler"/>
</http>

<bean id="myAccessDeniedHandler" class="package.MyAccessDeniedHandler">
    <!-- <constructor-arg ref="myInvalidSessionStrategy" /> -->
</bean>

MyAccessDeniedHandler:

public class MyAccessDeniedHandler implements AccessDeniedHandler {
    /* ... */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception)
            throws IOException, ServletException {
        if (exception instanceof MissingCsrfTokenException) {
            /* Handle as a session timeout (redirect, etc).
            Even better if you inject the InvalidSessionStrategy
            used by your SessionManagementFilter, like this:
            invalidSessionStrategy.onInvalidSessionDetected(request, response);
            */
        } else {
            /* Redirect to a error page, send HTTP 403, etc. */
        }
    }
}

另外,您可以将自定义处理程序定义为DelegatingAccessDeniedHandler

<bean id="myAccessDeniedHandler" class="org.springframework.security.web.access.DelegatingAccessDeniedHandler">
    <constructor-arg name="handlers">
        <map>
            <entry key="org.springframework.security.web.csrf.MissingCsrfTokenException">
                <bean class="org.springframework.security.web.session.InvalidSessionAccessDeniedHandler">
                    <constructor-arg name="invalidSessionStrategy" ref="myInvalidSessionStrategy" />
                </bean>
            </entry>
        </map>
    </constructor-arg>
    <constructor-arg name="defaultHandler">
        <bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
            <property name="errorPage" value="/my_error_page"/>
        </bean>
    </constructor-arg>
</bean>