Search code examples
springspring-mvcspring-securitycsrf-protection

Session timeout leads to Access Denied in Spring MVC when CSRF integration with Spring Security


I have Integrated CSRF token with Spring Security in my Spring MVC Project. Everything work properly with CSRF token, token will be send from client side to server side.

I have changed my logout process to make it POST method to send CSRF token and its works fine.

I have face problem when session timeout is occurred, it needs to be redirected to spring default logout URL but it gives me Access Denied on that URL.

How to override this behavior.

I have include below line in Security config file

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

Please let me know if anyone needs more information.


Solution

  • The question is a bit old, but answers are always useful.

    First, this is a known issue with session-backed CSRF tokens, as described in the docs: CSRF Caveats - Timeouts.

    To solve it, use some Javascript to detect imminent timeouts, use a session-independent CSRF token repository or create a custom AccessDeniedHandler route. I chose the latter:

    Config 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. */
            }
        }
    }
    

    Alternatively, you can define the custom handler as a 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>