Spring Security pour une API REST

Spring Security pour une API REST

1. Vue d'ensemble

Dans ce didacticiel, nous apprenons à utiliserSecure a REST API using Spring and Spring Security 5.

Nous allons définir la sécurité à l'aide de la configuration Java et utiliser une approche de connexion et cookie pour l'authentification.

Lectures complémentaires:

Tutoriel d'authentification de sécurité Spring

Comment créer un processus d'inscription de niveau production pour les nouveaux utilisateurs et un flux de connexion pour les utilisateurs existants.

Read more

Autorité accordée contre rôle dans la sécurité de printemps

Guide rapide sur la différence entre une autorité octroyée et un rôle dans Spring Security.

Read more

Authentification de base de Spring Security

Configurez l'authentification de base dans Spring - la configuration XML, les messages d'erreur et un exemple d'utilisation des URL sécurisées avec curl.

Read more

2. Activer Spring Security

The architecture of Spring Security is based entirely on Servlet Filters. Par conséquent, cela vient avant Spring MVC en ce qui concerne le traitement des requêtes HTTP.

L'option la plus simple pour enregistrer le filtre de sécurité Spring consiste à annoter notre classe de configuration avec@EnableWebSecurity:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

    // ...
}

Pour une application non-Spring Boot, nous pouvons étendre lesAbstractSecurityWebApplicationInitializer et passer notre classe de configuration dans son constructeur:

public class SecurityWebApplicationInitializer
  extends AbstractSecurityWebApplicationInitializer {

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

Ou bien nous pouvons le déclarer enweb.xml de l'application:


   springSecurityFilterChain
   org.springframework.web.filter.DelegatingFilterProxy


   springSecurityFilterChain
   /*

Nous devrions nommer le filtre‘springSecurityFilterChain' pour correspondre au bean par défaut créé par Spring Security dans le conteneur.

Notez que le filtre défini n'est pas la classe réelle implémentant la logique de sécurité. Au lieu de cela, c'est unDelegatingFilterProxy qui délègue les méthodes du filtre à un bean interne. Ceci est fait pour que le bean cible puisse toujours bénéficier du cycle de vie du contexte Spring et de sa flexibilité.

Le modèle d'URL utilisé pour configurer le filtre est/* même si tout le service Web est mappé sur/api/*. Cela donne à la configuration de sécurité une option pour sécuriser d'autres mappages possibles si nécessaire.

3. Configuration Java de Spring Security

Nous pouvons faire des configurations de sécurité entièrement dans une classe Java en créant une classe de configuration qui étendWebSecurityConfigurerAdapter et en l'annotant avec@EnableWebSecurity:

@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

    // ...
}

Maintenant, créons des utilisateurs avec différents rôles dansSecurityJavaConfig que nous utiliserons pour authentifier nos points de terminaison d'API:

@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();
}

Ensuite, configurons la sécurité pour nos points de terminaison API:

@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. L'élémenthttp

L'élémenthttp est l'élément de départ pour la configuration de la sécurité et nous fournit des méthodes fluides et flexibles pour configurer la sécurité.

Dans notre implémentation, nous créons des mappages sécurisés/api/foos and/api/admin/** usingantMatchers. 

Le modèle/api/foos est accessible à tout utilisateur authentifié. D'autre part,/api/admin/** will n'est accessible qu'aux utilisateurs deADMIN role.

3.2. Le point d'entrée

Dans une application Web standard, le processus d'authentification peut se déclencher automatiquement lorsqu'un client non authentifié tente d'accéder à une ressource sécurisée. Ce processus redirige généralement vers une page de connexion afin que l'utilisateur puisse entrer les informations d'identification.

Toutefois, pour un service Web REST, ce comportement n’a pas beaucoup de sens. 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 - il s'agit d'une partie obligatoire de la configuration et peut être injectée via la méthodeauthenticationEntryPoint .

Gardant à l'esprit que cette fonctionnalité n'a pas de sens dans le contexte du service REST, nous définissons le nouveau point d'entrée personnalisé comme renvoyant simplement 401 lorsqu'il est déclenché:

@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");
    }
}

Une remarque rapide ici est que le 401 est envoyé sans l'en-têteWWW-Authenticate, comme requis par la spécification HTTP. Bien entendu, nous pouvons définir la valeur manuellement si nous en avons besoin.

3.3. Le formulaire de connexion pour REST

Il existe plusieurs façons de procéder à l'authentification pour une API REST. L'une des valeurs par défaut fournies par Spring Security est la connexion par formulaire - qui utilise un filtre de traitement d'authentification -org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.

The formLogin element will create this filter and also provides additional methods successHandler and failureHandler pour définir respectivement nos gestionnaires de réussite et d'échec d'authentification personnalisés.

Notez que pour une application Web standard, l'annotation@EnableWebSecurity configure elle-même un grand nombre de configurations par défaut.

3.4. L'authentification doit renvoyer 200 au lieu de 301

Par défaut, la connexion par formulaire répondra à une demande d'authentification réussie avec un code d'état301 MOVED PERMANENTLY; cela a du sens dans le contexte d'un formulaire de connexion réel qui doit être redirigé après la connexion.

Cependant,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 sous la forme de filtre de connexion, pour remplacer celui par défaut. Le nouveau gestionnaire implémente exactement la même connexion que lesorg.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler par défaut avec une différence notable - il supprime la logique de redirection:

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. L'authentification a échoué devrait renvoyer 401 au lieu de 302

De même, nous avons configuré le gestionnaire d'échec d'authentification, comme nous l'avons fait avec le gestionnaire de réussite.

Heureusement - dans ce cas, nous n'avons pas besoin de définir une nouvelle classe pour ce gestionnaire - l'implémentation standard -SimpleUrlAuthenticationFailureHandler - fait très bien.

3.6. Le gestionnaire et fournisseur d'authentification

Le processus d'authentification utilise unin-memory provider pour effectuer l'authentification. Ceci est destiné à simplifier la configuration car une implémentation de production de ces artefacts sort du cadre de cet article.

Nous avons créé deux utilisateurs à savoiruser having roleUSER andadmin with roleADMIN.

3.7. Enfin - Authentification par rapport au service REST en cours d'exécution

Voyons maintenant comment nous pouvons nous authentifier auprès de l'API REST pour différents utilisateurs.

L'URL de connexion est/login - et une simple commandecurl effectuant la connexion pour l'utilisateur du nomuser et du mot de passeuserPass serait:

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

Cette demande renverra le cookie que nous pourrons utiliser pour toute demande ultérieure relative au service REST.

Nous pouvons utilisercurl pour l'authentification etstore the cookie it receives in a file:

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

Puiswe can use the cookie from the file pour faire d'autres requêtes authentifiées:

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

Puisque le scanuser accède au point d'envoi/api/foos/ , cette requête authentifiée aboutira correctement * à un 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"}]

De même, pouradmin user, nous pouvons utilisercurl pour l'authentification:

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

puis des cookies mis à jour pour accéder aux points de terminaison d'administration/api/admin/*:

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

Puisqueadmin user peut accéder au point final/api/admin/* , cela entraînera une réponse réussie:

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. La configuration de sécurité XML

Nous pouvons également faire toute la configuration de sécurité ci-dessus en utilisant XML au lieu de la configuration Java:


    

    

    






    
        
            
            
        
    

La majeure partie de la configuration est effectuée à l'aide de l'espace de noms de sécurité. Pour cela, nous devons définir les emplacements du schéma.

L'espace de noms est conçu de manière à exprimer les cas d'utilisation courants de Spring Security tout en fournissant des hameçons pour les beans bruts afin de prendre en charge des scénarios plus avancés.

5. Conclusion

Dans ce tutoriel, nous avons abordé la configuration et la mise en œuvre de la sécurité de base pour un service RESTful utilisant Spring Security 5.

Nous avons appris à configurer la sécurité pour notre API REST entièrement via la configuration Java et avons également examiné son alternative de configurationweb.xml.

Nous avons ensuite expliqué comment créer des utilisateurs et des rôles pour notre application sécurisée, ainsi que la manière de les mapper vers des points de terminaison spécifiques de notre application.

Enfin, nous avons également envisagé de créer un point d’entrée d’authentification personnalisé et un gestionnaire de réussite personnalisé, qui confèrent à notre application une plus grande flexibilité en termes de contrôle de la sécurité.

L'implémentation complète est disponibleover on Github.