2014-12-26 6 views
8

I token CSRF integrato con Spring Security nel mio progetto MVC Spring. Tutto funziona correttamente con il token CSRF, il token verrà inviato dal lato client al lato server.Timeout sessione porta ad Accesso negato in Spring MVC quando l'integrazione CSRF con Spring Security

Ho modificato il processo logout per renderlo il metodo POST per inviare il token CSRF e funziona correttamente.

Ho un problema con il volto quando si verifica il timeout della sessione, deve essere reindirizzato all'URL di logout predefinito di primavera ma mi dà Access Denied su quell'URL.

Come ignorare questo comportamento.

devo includere sotto la linea nel file di configurazione di sicurezza

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

Per favore fatemi sapere se qualcuno ha bisogno di ulteriori informazioni.

risposta

12

La domanda è un po 'vecchia, ma le risposte sono sempre utili.

Innanzitutto, si tratta di un problema noto con i token CSRF con sessione supportata, come descritto nei documenti: CSRF Caveats - Timeouts.

Per risolverlo, utilizzare alcuni Javascript per rilevare i timeout imminenti, utilizzare un repository di token CSRF indipendente dalla sessione o creare una route personalizzata AccessDeniedHandler. Ho scelto la seconda:

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

In alternativa, è possibile definire il gestore personalizzato come 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> 
+0

Grazie ... ho già fatto .. –

0

La risposta fornita di mdrg è giusto, e ho anche implementato un custom AccessDeniedHandler che Sottopongo alla vostra attenzione:

import java.io.IOException; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 
import org.springframework.security.access.AccessDeniedException; 
import org.springframework.security.web.access.AccessDeniedHandlerImpl; 
import org.springframework.security.web.csrf.MissingCsrfTokenException; 
import org.springframework.security.web.savedrequest.HttpSessionRequestCache; 
import org.springframework.security.web.savedrequest.RequestCache; 

/** 
* Intended to fix the CSRF Timeout Caveat 
* (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-timeouts). 
* When the session expires and a request requiring CSRF is received (POST), the 
* missing token exception is handled by caching the current request and 
* redirecting the user to the login page after which their original request will 
* complete. The intended result is that no loss of data due to the timeout will 
* occur. 
*/ 
public class MissingCsrfTokenAccessDeniedHandler extends AccessDeniedHandlerImpl { 
    private RequestCache requestCache = new HttpSessionRequestCache(); 
    private String loginPage = "/login"; 

    @Override 
    public void handle(HttpServletRequest req, HttpServletResponse res, AccessDeniedException exception) throws IOException, ServletException { 
    if (exception instanceof MissingCsrfTokenException && isSessionInvalid(req)) { 
     requestCache.saveRequest(req, res); 
     res.sendRedirect(req.getContextPath() + loginPage); 
    } 
    super.handle(req, res, exception); 
    } 

    private boolean isSessionInvalid(HttpServletRequest req) { 
    try { 
     HttpSession session = req.getSession(false); 
     return session == null || !req.isRequestedSessionIdValid(); 
    } 
    catch (IllegalStateException ex) { 
     return true; 
    } 
    } 

    public void setRequestCache(RequestCache requestCache) { 
    this.requestCache = requestCache; 
    } 

    public void setLoginPage(String loginPage) { 
    this.loginPage = loginPage; 
    } 
} 

cablata tramite Java config:

@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
    ... 
    http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler()); 
    ... 
    } 

    public AccessDeniedHandler getAccessDeniedHandler() { 
    return new MissingCsrfTokenAccessDeniedHandler(); 
    } 
}