Utilisation de JWT avec Spring Security OAuth

Utilisation de JWT avec Spring Security OAuth

1. Vue d'ensemble

Dans ce didacticiel, nous allons expliquer comment faire en sorte que notre implémentation Spring Security OAuth2 utilise les jetons Web JSON.

Nous continuons également à développer lesthe previous article de cette série OAuth.

Lectures complémentaires:

Déconnexion dans une application sécurisée OAuth

Découvrez comment implémenter la déconnexion dans une application Spring Security OAuth2 avec JWT.

Read more

OAuth2 Se souvenir de moi avec le jet de rafraîchissement

Apprenez à implémenter la fonctionnalité Remember-me avec une interface angulaire, pour une application sécurisée avec Spring Security et OAuth2.

Read more

OAuth2 pour une API REST de printemps - Gestion du jeton d'actualisation dans AngularJS

Nous avons appris comment stocker le jeton d'actualisation dans une application cliente AngularJS, comment actualiser un jeton d'accès expiré et comment exploiter le proxy Zuul.

Read more

 

Avant de commencer - une note importante. Gardez à l'esprit quethe Spring Security core team is in the process of implementing a new OAuth2 stack - avec certains aspects déjà sortis et d'autres encore en cours.

Voici une courte vidéo qui vous donnera un aperçu de cet effort:

 

Très bien, allons-y.

2. Configuration Maven

Tout d'abord, nous devons ajouter la dépendancespring-security-jwt à nospom.xml:


    org.springframework.security
    spring-security-jwt

Notez que nous devons ajouter la dépendancespring-security-jwt à la fois au serveur d'autorisation et au serveur de ressources.

3. Serveur d'autorisation

Ensuite, nous allons configurer notre serveur d'autorisation pour utiliserJwtTokenStore - comme suit:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
      throws Exception {
        endpoints.tokenStore(tokenStore())
                 .accessTokenConverter(accessTokenConverter())
                 .authenticationManager(authenticationManager);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
}

Notez que nous avons utilisé unsymmetric key dans nosJwtAccessTokenConverter pour signer nos jetons - ce qui signifie que nous devrons également utiliser la même clé exacte pour le serveur de ressources.

4. Serveur de ressources

Voyons maintenant la configuration de notre serveur de ressources, qui est très similaire à la configuration du serveur d’autorisations:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }
}

Gardez à l'esprit que nous définissons ces deux serveurs comme étant entièrement séparés et déployables indépendamment. C’est la raison pour laquelle nous devons déclarer à nouveau certains des mêmes beans ici, dans la nouvelle configuration.

5. Revendications personnalisées dans le jeton

Configurons maintenant une infrastructure pour pouvoir ajouter quelquescustom claims in the Access Token. Les revendications standard fournies par le framework sont toutes bonnes, mais la plupart du temps, nous aurons besoin d'informations supplémentaires dans le jeton à utiliser côté client.

Nous allons définir unTokenEnhancer pour personnaliser notre jeton d'accès avec ces revendications supplémentaires.

Dans l'exemple suivant, nous ajouterons un champ supplémentaire «organization» à notre jeton d'accès - avec ceCustomTokenEnhancer:

public class CustomTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(
      OAuth2AccessToken accessToken,
      OAuth2Authentication authentication) {
        Map additionalInfo = new HashMap<>();
        additionalInfo.put(
          "organization", authentication.getName() + randomAlphabetic(4));
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(
          additionalInfo);
        return accessToken;
    }
}

Ensuite, nous allons câbler cela dans notre configurationAuthorization Server - comme suit:

@Override
public void configure(
  AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(
      Arrays.asList(tokenEnhancer(), accessTokenConverter()));

    endpoints.tokenStore(tokenStore())
             .tokenEnhancer(tokenEnhancerChain)
             .authenticationManager(authenticationManager);
}

@Bean
public TokenEnhancer tokenEnhancer() {
    return new CustomTokenEnhancer();
}

Avec cette nouvelle configuration opérationnelle, voici à quoi ressemblerait une charge utile de jeton de jeton:

{
    "user_name": "john",
    "scope": [
        "foo",
        "read",
        "write"
    ],
    "organization": "johnIiCh",
    "exp": 1458126622,
    "authorities": [
        "ROLE_USER"
    ],
    "jti": "e0ad1ef3-a8a5-4eef-998d-00b26bc2c53f",
    "client_id": "fooClientIdPassword"
}

5.1. Utilisez le jeton d'accès dans le client JS

Enfin, nous souhaitons utiliser les informations du jeton dans notre application client AngualrJS. Nous utiliserons la bibliothèqueangular-jwt pour cela.

Nous allons donc utiliser la revendication «organization» dans nosindex.html: