6

Ich verwende Spring MVC/Security 3.X. Das Problem ist, dass ich immer 403 auf der Anmeldeseite bekomme das Session-Timeout, wo unter „InvalidCsrfTokenException“ von Spring-Framework geworfen wird:Wie behandelt man das ungültige CSRF-Token, das in der Anfrage gefunden wird, wenn Sitzungszeitlimit in der Spring-Sicherheit

threw exception [org.springframework.security.web.csrf.InvalidCsrfTokenException: Invalid CSRF Token '7b4aefe9-6685-4c70-adf1-0d633680523a' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.] with root cause 
org.springframework.security.web.csrf.InvalidCsrfTokenException: Invalid CSRF Token '7b4aefe9-6685-4c70-adf1-0d633680523a' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'. 
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) 
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) 
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) 
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:119) 
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) 
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 

Wie es im Frühjahr Dokumentation erwähnt wird die CSRF timeout ist ein Problem, das behandelt werden soll eine Möglichkeit, dieses Szenario zu behandeln ist zu haben. benutzerdefinierter AccessDeniedHandler, in dem wir eine CSRF-Ausnahme abfangen, zum Beispiel:

static class CustomAccessDeniedHandler extends AccessDeniedHandlerImpl{ 

    @Override 
    public void handle(HttpServletRequest request, 
      HttpServletResponse response, 
      AccessDeniedException accessDeniedException) 
      throws IOException, ServletException { 
     if (accessDeniedException instanceof MissingCsrfTokenException 
       || accessDeniedException instanceof InvalidCsrfTokenException) { 

      //What goes in here??? 

     } 

     super.handle(request, response, accessDeniedException); 

    } 
} 

Frage: Was ist der beste Weg, um diese Situation zu bewältigen, ohne die Seite zu aktualisieren (was schlechte Benutzererfahrung ist) oder eine endlose Sitzung zu haben? Danke im Vorraus für deine Hilfe.

+0

Machst du einen AJAX-Anruf? – giubueno

+0

Ich würde empfehlen, einen Refresh-HTTP-Response-Header zu senden, der die Seite direkt nach Ablauf der Sitzung aktualisiert, sie neu lädt und eine neue Sitzung startet. Sie könnten auch einige Javascript auf Ihren Seiten ausführen, um einen Benutzer zu warnen, wenn die Sitzung kurz vor dem Ablauf steht. –

+0

Ja, ich benutze AJAX-Anruf. Wie ich bereits erwähnt habe, möchte ich eine endlose Erneuerungssitzung vermeiden. Der Refresh-Kopf entspricht also nicht meinen Anforderungen. JS, um den Benutzer zu warnen, dass die Sitzung bald ablaufen wird, ist eine gute Idee, aber für die Anmeldeseite. –

Antwort

4

Der einfachste Weg, ich Griff Invalidier CSRF-Token gefunden, wenn die Sitzungszeitüberschreitung auf der Login-Seite eine der folgenden ist:

  1. die Anfrage umleiten wieder die Anmeldeseite wieder vi CustomAccessDeniedHandler:

    static class CustomAccessDeniedHandler extends AccessDeniedHandlerImpl{ 
    
    
    
    
        @Override 
        public void handle(HttpServletRequest request, 
          HttpServletResponse response, 
    
        AccessDeniedException accessDeniedException) 
        throws IOException, ServletException { 
    if (accessDeniedException instanceof MissingCsrfTokenException 
         || accessDeniedException instanceof InvalidCsrfTokenException) { 
    
        if(request.getRequestURI().contains("login")){ 
         response.sendRedirect(request.getContextPath()+"/login");           
        } 
    } 
    
    super.handle(request, response, accessDeniedException); 
    
    
    
    } 
    } 
    
  2. Ad d Refresh-Header als Neil McGuigan vorgeschlagen:

<meta http-equiv="refresh" content="${pageContext.session.maxInactiveInterval}">

  1. Außerdem müssen Sie eine Bohne für die neuen CustomAccessDeniedHandler erstellen und registrieren. Das folgende Beispiel zeigt dies für die Java-Konfiguration.

In jeder Konfigurationsklasse:

@Bean 
public AccessDeniedHandler accessDeniedHandler() { 
    return new CustomAccessDeniedHandler(); 
} 

In Ihren Sicherheits Config das configure Verfahren wie folgt ändern:

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

Auch here sehen.

eine bessere Lösung für Spring-Sicherheit, um diese Situation in ihrem Rahmen zu behandeln.

-1

Wenn Spring Security verwenden, müssen Sie die '_csrf' senden, gibt es folgende Möglichkeiten:

In Form:

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

In Ajax:

<head> <meta name="_csrf" content="${_csrf.token}"/> <!-- default header name is X-CSRF-TOKEN --> <meta name="_csrf_header" content="${_csrf.headerName}"/> <!-- ... --> </head>

$(function() { 
    var token = $("meta[name='_csrf']").attr("content"); 
    var header = $("meta[name='_csrf_header']").attr("content"); 
    $(document).ajaxSend(function(e, xhr, options) { 
    xhr.setRequestHeader(header, token); 
    }); 
}); 

Quelle: http://docs.spring.io/autorepo/docs/spring-security/3.2.0.CI-SNAPSHOT/reference/html/csrf.html

+0

Ich mache das bereits. Das Problem, das ich habe, ist, dass CSRF-Token wird immer ungültig, wenn die Sitzung ausläuft, die Ergebnis in unangenehmes Verhalten in der LOGIN-Seite. –