Spring Security - Nach dem Login zur vorherigen URL umleiten

Spring Security - Nach der Anmeldung zur vorherigen URL umleiten

1. Überblick

Dieser Artikel konzentriert sich aufhow to redirect a user back to the originally requested URL – after they log in.

Zuvor haben wirhow to redirect to different pages after login with Spring Security für verschiedene Benutzertypen gesehen und verschiedene Arten vonredirections with Spring MVC behandelt.

Der Artikel basiert auf dem Tutorial vonSpring Security Login.

2. Gang und gäbe sein

Die gängigsten Methoden zum Implementieren der Umleitungslogik nach dem Anmelden sind:

  • mitHTTP Referer Header

  • Speichern der ursprünglichen Anforderung in der Sitzung

  • Anhängen der ursprünglichen URL an die umgeleitete Anmelde-URL

Using the HTTP Referer header ist ein einfacher Weg, für die meisten Browser undHTTP Clients setzenReferer automatisch. DaReferer jedoch fälschbar ist und von der Client-Implementierung abhängt, wird die Verwendung desHTTP Referer-Headers zur Implementierung der Umleitung im Allgemeinen nicht empfohlen.

Saving the original request in the session ist eine sichere und robuste Methode, um diese Art der Umleitung zu implementieren. Neben der ursprünglichen URL können wir ursprüngliche Anforderungsattribute und benutzerdefinierte Eigenschaften in der Sitzung speichern.

And appending original URL to the redirected login URL wird normalerweise in SSO-Implementierungen gesehen. Bei der Authentifizierung über einen SSO-Dienst werden Benutzer mit der angehängten URL auf die ursprünglich angeforderte Seite umgeleitet. Wir müssen sicherstellen, dass die angehängte URL richtig codiert ist.

Eine andere ähnliche Implementierung besteht darin, die ursprüngliche Anforderungs-URL in einem ausgeblendeten Feld innerhalb des Anmeldeformulars abzulegen. Dies ist jedoch nicht besser als die Verwendung vonHTTP Referer

In Spring Security werden die ersten beiden Ansätze nativ unterstützt.

3. AuthenticationSuccessHandler

Bei der formularbasierten Authentifizierung erfolgt die Umleitung direkt nach der Anmeldung, die in einerAuthenticationSuccessHandler-Instanz inSpring Security ausgeführt wird.

Es werden drei Standardimplementierungen bereitgestellt:SimpleUrlAuthenticationSuccessHandler,SavedRequestAwareAuthenticationSuccessHandler undForwardAuthenticationSuccessHandler. Wir konzentrieren uns auf die ersten beiden Implementierungen.

3.1. SavedRequestAwareAuthenticationSuccessHandler

SavedRequestAwareAuthenticationSuccessHandler verwendet die in der Sitzung gespeicherte gespeicherte Anforderung. Nach einer erfolgreichen Anmeldung werden Benutzer zu der in der ursprünglichen Anforderung gespeicherten URL umgeleitet.

Für die Formularanmeldung wirdSavedRequestAwareAuthenticationSuccessHandler als StandardAuthenticationSuccessHandler verwendet.

@Configuration
@EnableWebSecurity
public class RedirectionSecurityConfig extends WebSecurityConfigurerAdapter {

    //...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
          .antMatchers("/login*")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin();
    }

}

Und das äquivalente XML wäre:


    
    
    

Angenommen, wir haben eine gesicherte Ressource am Standort "/ secured". Beim ersten Zugriff auf die Ressource werden wir zur Anmeldeseite weitergeleitet. Nach dem Ausfüllen der Anmeldeinformationen und dem Absenden des Anmeldeformulars werden wir zurück zu unserem ursprünglich angeforderten Ressourcenort geleitet:

@Test
public void givenAccessSecuredResource_whenAuthenticated_thenRedirectedBack()
  throws Exception {

    MockHttpServletRequestBuilder securedResourceAccess = get("/secured");
    MvcResult unauthenticatedResult = mvc
      .perform(securedResourceAccess)
      .andExpect(status().is3xxRedirection())
      .andReturn();

    MockHttpSession session = (MockHttpSession) unauthenticatedResult
      .getRequest()
      .getSession();
    String loginUrl = unauthenticatedResult
      .getResponse()
      .getRedirectedUrl();
    mvc
      .perform(post(loginUrl)
        .param("username", userDetails.getUsername())
        .param("password", userDetails.getPassword())
        .session(session)
        .with(csrf()))
      .andExpect(status().is3xxRedirection())
      .andExpect(redirectedUrlPattern("**/secured"))
      .andReturn();

    mvc
      .perform(securedResourceAccess.session(session))
      .andExpect(status().isOk());
}

3.2. SimpleUrlAuthenticationSuccessHandler

Im Vergleich zuSavedRequestAwareAuthenticationSuccessHandler bietetSimpleUrlAuthenticationSuccessHandler mehr Optionen für Umleitungsentscheidungen.

Wir können die refererbasierte Umleitung umsetUserReferer(true) aktivieren:

public class RefererRedirectionAuthenticationSuccessHandler
  extends SimpleUrlAuthenticationSuccessHandler
  implements AuthenticationSuccessHandler {

    public RefererRedirectionAuthenticationSuccessHandler() {
        super();
        setUseReferer(true);
    }

}

Verwenden Sie es dann alsAuthenticationSuccessHandler inRedirectionSecurityConfig:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .antMatchers("/login*")
      .permitAll()
      .anyRequest()
      .authenticated()
      .and()
      .formLogin()
      .successHandler(new RefererAuthenticationSuccessHandler());
}

Und für die XML-Konfiguration:


    
    
    


3.3. Unter der Haube

Diese benutzerfreundlichen Funktionen inSpring Security enthalten keine Magie. Wenn eine gesicherte Ressource angefordert wird, wird die Anforderung durch eine Kette verschiedener Filter gefiltert. Authentifizierungsprinzipien und -berechtigungen werden überprüft. Wenn die Anforderungssitzung noch nicht authentifiziert ist, werdenAuthenticationException ausgelöst.

DieAuthenticationException werden in denExceptionTranslationFilter, abgefangen, in denen ein Authentifizierungsprozess gestartet wird, was zu einer Umleitung zur Anmeldeseite führt.

public class ExceptionTranslationFilter extends GenericFilterBean {

    //...

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
        //...

        handleSpringSecurityException(request, response, chain, ase);

        //...
    }

    private void handleSpringSecurityException(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, RuntimeException exception)
      throws IOException, ServletException {

        if (exception instanceof AuthenticationException) {

            sendStartAuthentication(request, response, chain,
              (AuthenticationException) exception);

        }

        //...
    }

    protected void sendStartAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain,
      AuthenticationException reason) throws ServletException, IOException {

       SecurityContextHolder.getContext().setAuthentication(null);
       requestCache.saveRequest(request, response);
       authenticationEntryPoint.commence(request, response, reason);
    }

    //...

}

Nach der Anmeldung können wir das Verhalten inAuthenticationSuccessHandler anpassen, wie oben gezeigt.

4. Fazit

In diesem Beispiel vonSpring Securityhaben wir die gängige Vorgehensweise für die Umleitung nach der Anmeldung erläutert und Implementierungen mit Spring Security erläutert.

Beachten Sie, dassall the implementations we mentioned are vulnerable to certain attacks if no validation or extra method controls are applied. Durch solche Angriffe werden Benutzer möglicherweise auf eine schädliche Website umgeleitet.

OWASP hatcheat sheet bereitgestellt, um uns bei der Bearbeitung nicht validierter Weiterleitungen und Weiterleitungen zu helfen. Dies würde sehr hilfreich sein, wenn wir Implementierungen selbst erstellen müssen.

Der vollständige Implementierungscode dieses Artikels befindet sich inover on Github.