Spring Security für eine REST-API

Frühjahrssicherheit für eine REST-API

1. Überblick

In diesem Tutorial lernen wir, wie manSecure a REST API using Spring and Spring Security 5 macht.

Wir richten die Sicherheit mithilfe der Java-Konfiguration ein und verwenden zur Authentifizierung einen Login- und Cookie-Ansatz.

Weitere Lektüre:

Spring Security Authentication Tutorial

Erstellen eines Registrierungsprozesses in Produktionsqualität für neue Benutzer und eines Anmeldeflusses für vorhandene Benutzer.

Read more

Gewährte Autorität versus Rolle im Frühjahr Sicherheit

Ein kurzer Überblick über den Unterschied zwischen einer erteilten Berechtigung und einer Rolle in Spring Security.

Read more

Basisauthentifizierung von Spring Security

Richten Sie im Frühjahr die Standardauthentifizierung ein - die XML-Konfiguration, die Fehlermeldungen und ein Beispiel für die Verwendung der gesicherten URLs mit curl.

Read more

2. Aktivieren Sie die Federsicherheit

The architecture of Spring Security is based entirely on Servlet Filters. Daher kommt dies vor Spring MVC in Bezug auf die Verarbeitung von HTTP-Anfragen.

Die einfachste Möglichkeit, den Spring-Sicherheitsfilter zu registrieren, besteht darin, unsere Konfigurationsklasse mit@EnableWebSecurity zu versehen:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

    // ...
}

Für eine Nicht-Spring-Boot-Anwendung können wirAbstractSecurityWebApplicationInitializer erweitern und unsere Konfigurationsklasse in ihrem Konstruktor übergeben:

public class SecurityWebApplicationInitializer
  extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(SecurityJavaConfig.class);
    }
}

Oder wir können es inweb.xml der Anwendung deklarieren:


   springSecurityFilterChain
   org.springframework.web.filter.DelegatingFilterProxy


   springSecurityFilterChain
   /*

Wir sollten den Filter‘springSecurityFilterChain' so benennen, dass er mit der von Spring Security im Container erstellten Standard-Bean übereinstimmt.

Beachten Sie, dass der definierte Filter nicht die eigentliche Klasse ist, die die Sicherheitslogik implementiert. Stattdessen delegierenDelegatingFilterProxydie Methoden des Filters an eine interne Bean. Dies geschieht, damit die Ziel-Bean weiterhin vom Lebenszyklus und der Flexibilität des Spring-Kontexts profitieren kann.

Das zum Konfigurieren des Filters verwendete URL-Muster lautet/*, obwohl der gesamte Webdienst/api/* zugeordnet ist. Dies gibt der Sicherheitskonfiguration die Möglichkeit, bei Bedarf auch andere mögliche Zuordnungen zu sichern.

3. Spring Security Java-Konfiguration

Wir können Sicherheitskonfigurationen vollständig in einer Java-Klasse durchführen, indem wir eine Konfigurationsklasse erstellen, dieWebSecurityConfigurerAdapter erweitert und mit@EnableWebSecurity kommentiert:

@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

    // ...
}

Erstellen wir nun Benutzer mit unterschiedlichen Rollen inSecurityJavaConfig, die wir zur Authentifizierung unserer API-Endpunkte verwenden:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
        .and()
        .withUser("user").password(encoder().encode("userPass")).roles("USER");
}

@Bean
public PasswordEncoder  encoder() {
    return new BCryptPasswordEncoder();
}

Als Nächstes konfigurieren wir die Sicherheit für unsere API-Endpunkte:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    .csrf().disable()
    .exceptionHandling()
    .authenticationEntryPoint(restAuthenticationEntryPoint)
    .and()
    .authorizeRequests()
    .antMatchers("/api/foos").authenticated()
    .antMatchers("/api/admin/**").hasRole("ADMIN")
    .and()
    .formLogin()
    .successHandler(mySuccessHandler)
    .failureHandler(myFailureHandler)
    .and()
    .logout();
}

3.1. Dashttp-Element

Dashttp-Element ist das Startelement für die Sicherheitskonfiguration und bietet uns fließende und flexible Methoden zur Konfiguration der Sicherheit.

In unserer Implementierung erstellen wir gesicherte Zuordnungen/api/foos and/api/admin/** usingantMatchers. 

Das Muster/api/foosist für jeden authentifizierten Benutzer zugänglich. Andererseits kann/api/admin/** nurADMIN role-Benutzern zugänglich sein.

3.2. Der Einstiegspunkt

In einer Standardwebanwendung wird der Authentifizierungsprozess möglicherweise automatisch ausgelöst, wenn ein nicht authentifizierter Client versucht, auf eine gesicherte Ressource zuzugreifen. Dieser Vorgang wird normalerweise auf eine Anmeldeseite umgeleitet, damit der Benutzer Anmeldeinformationen eingeben kann.

Für einen REST-Webdienst ist dieses Verhalten jedoch wenig sinnvoll. We should be able to authenticate only by a request to the correct URI and if the user is not authenticated all requests should simply fail with a 401 UNAUTHORIZED status code.

Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point - Dies ist ein erforderlicher Teil der Konfiguration und kann über die MethodeauthenticationEntryPoint injiziert werden.

Unter Berücksichtigung dessen, dass diese Funktionalität im Kontext des REST-Service keinen Sinn ergibt, definieren wir den neuen benutzerdefinierten Einstiegspunkt, um bei Auslösung einfach 401 zurückzugeben:

@Component
public final class RestAuthenticationEntryPoint
  implements AuthenticationEntryPoint {

    @Override
    public void commence(
        HttpServletRequest request,
        HttpServletResponse response,
        AuthenticationException authException) throws IOException {

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
          "Unauthorized");
    }
}

Eine kurze Randnotiz hier ist, dass der 401 ohne den HeaderWWW-Authenticate gesendet wird, wie in der HTTP-Spezifikation gefordert. Wir können den Wert natürlich manuell einstellen, wenn wir müssen.

3.3. Das Anmeldeformular für REST

Es gibt mehrere Möglichkeiten, die Authentifizierung für eine REST-API durchzuführen. Eine der Standardeinstellungen von Spring Security ist die Formularanmeldung, bei der ein Authentifizierungsverarbeitungsfilter verwendet wird -org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.

The formLogin element will create this filter and also provides additional methods successHandler and failureHandler, um unsere benutzerdefinierten Authentifizierungserfolgs- und Fehlerbehandlungsroutinen festzulegen.

Beachten Sie, dass für eine Standard-Webanwendung die Annotation@EnableWebSecurityviele Standardkonfigurationen selbst konfiguriert.

3.4. Die Authentifizierung sollte 200 statt 301 zurückgeben

Standardmäßig beantwortet die Formularanmeldung eine erfolgreiche Authentifizierungsanforderung mit dem Statuscode301 MOVED PERMANENTLY. Dies ist im Kontext eines tatsächlichen Anmeldeformulars sinnvoll, das nach dem Anmelden umgeleitet werden muss.

for a RESTful web service, the desired response for a successful authentication should be 200 OK.

We do this by injecting a custom authentication success handler im Formularanmeldefilter, um den Standardfilter zu ersetzen. Der neue Handler implementiert genau das gleiche Login wie das Standard-org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler mit einem bemerkenswerten Unterschied - er entfernt die Umleitungslogik:

public class MySavedRequestAwareAuthenticationSuccessHandler
  extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(
      HttpServletRequest request,
      HttpServletResponse response,
      Authentication authentication)
      throws ServletException, IOException {

        SavedRequest savedRequest
          = requestCache.getRequest(request, response);

        if (savedRequest == null) {
            clearAuthenticationAttributes(request);
            return;
        }
        String targetUrlParam = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl()
          || (targetUrlParam != null
          && StringUtils.hasText(request.getParameter(targetUrlParam)))) {
            requestCache.removeRequest(request, response);
            clearAuthenticationAttributes(request);
            return;
        }

        clearAuthenticationAttributes(request);
    }

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

3.5. Die fehlgeschlagene Authentifizierung sollte 401 anstelle von 302 zurückgeben

In ähnlicher Weise haben wir den Authentifizierungsfehler-Handler wie den Erfolgs-Handler konfiguriert.

Glücklicherweise müssen wir in diesem Fall keine neue Klasse für diesen Handler definieren - die Standardimplementierung -SimpleUrlAuthenticationFailureHandler - funktioniert einwandfrei.

3.6. Der Authentifizierungsmanager und -anbieter

Der Authentifizierungsprozess verwendet einin-memory provider, um die Authentifizierung durchzuführen. Dies soll die Konfiguration vereinfachen, da eine produktive Implementierung dieser Artefakte nicht in diesem Artikel enthalten ist.

Wir haben zwei Benutzer erstellt, nämlichuser RasierrolleUSER andadmin mit RolleADMIN.

3.7. Endlich - Authentifizierung gegen den laufenden REST-Service

Nun wollen wir sehen, wie wir uns für verschiedene Benutzer bei der REST-API authentifizieren können.

Die URL für die Anmeldung lautet/login - und ein einfacher Befehlcurl, der die Anmeldung für den Benutzer mit dem Namenuser und dem KennwortuserPass durchführt, lautet:

curl -i -X POST -d username=user -d password=userPass
http://localhost:8080/spring-security-rest/login

Diese Anfrage gibt das Cookie zurück, das wir für jede nachfolgende Anfrage gegen den REST-Service verwenden können.

Wir könnencurl zur Authentifizierung undstore the cookie it receives in a file verwenden:

curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login

Dann musswe can use the cookie from the fileweitere authentifizierte Anforderungen ausführen:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/foos

Da deruser -Scan auf den/api/foos/ -Sendpunkt zugreift, führt diese authentifizierte Anforderung korrekt * zu einem 200 OK:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 24 Jul 2013 20:31:13 GMT

[{"id":0,"name":"JbidXc"}]

In ähnlicher Weise können wir füradmin usercurl zur Authentifizierung verwenden:

curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login

und dann aktualisierte Cookies für den Zugriff auf Administratorendpunkte/api/admin/*:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/admin/x

Daadmin user auf den Endpunkt/api/admin/* zugreifen kann, führt dies zu einer erfolgreichen Antwort:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 5
Date: Mon, 15 Oct 2018 17:16:39 GMT

Hello

4. Die XML-Sicherheitskonfiguration

Wir können alle oben genannten Sicherheitskonfigurationen auch mit XML anstelle der Java-Konfiguration durchführen:


    

    

    






    
        
            
            
        
    

Der größte Teil der Konfiguration erfolgt über den Sicherheitsnamespace. Um dies zu ermöglichen, müssen wir die Schemastellen definieren.

Der Namespace ist so konzipiert, dass er die üblichen Anwendungsfälle von Spring Security zum Ausdruck bringt und gleichzeitig Hooks für Raw Beans bereitstellt, um fortgeschrittenere Szenarien zu berücksichtigen.

5. Fazit

In diesem Lernprogramm wurde die grundlegende Sicherheitskonfiguration und -implementierung für einen RESTful-Service mit Spring Security 5 behandelt.

Wir haben gelernt, wie die Sicherheitskonfiguration für unsere REST-API vollständig über die Java-Konfiguration durchgeführt wird, und haben uns auch die Konfigurationsalternative vonweb.xmlangesehen.

Als Nächstes wurde erläutert, wie Sie Benutzer und Rollen für unsere gesicherte Anwendung erstellen und diese Benutzer bestimmten Endpunkten unserer Anwendung zuordnen.

Schließlich haben wir uns auch mit der Erstellung eines benutzerdefinierten Authentifizierungseintrittspunkts und eines benutzerdefinierten Erfolgshandlers befasst, die unserer Anwendung eine größere Flexibilität bei der Steuerung der Sicherheit verleihen.

Die vollständige Implementierung ist inover on Github verfügbar.