Extraction du principal et des autorités à l’aide de Spring Security OAuth

Extraction du principal et des autorités à l'aide de Spring Security OAuth

1. Vue d'ensemble

Dans ce didacticiel, nous allons illustrer comment créer une application qui délègue l'authentification utilisateur à un tiers, ainsi qu'à un serveur d'autorisation personnalisé, à l'aide de Spring Boot et Spring Security OAuth.

Aussi,we’ll demonstrate how to extract both Principal and Authorities using Spring’s PrincipalExtractor and AuthoritiesExtractor interfaces.

Pour une introduction à Spring Security OAuth2, veuillez consulter les articles dethese.

2. Dépendances Maven

Pour commencer, nous devons ajouter la dépendancespring-security-oauth2-autoconfigure à nospom.xml:


    org.springframework.security.oauth.boot
    spring-security-oauth2-autoconfigure
    2.0.1.RELEASE

3. Authentification OAuth avec Github

Ensuite, créons la configuration de sécurité de notre application:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http)
      throws Exception {

        http.antMatcher("/**")
          .authorizeRequests()
          .antMatchers("/login**")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin().disable();
    }
}

En bref, nous disons que n'importe qui peut accéder au point de terminaison/login et que tous les autres points de terminaison nécessiteront une authentification de l'utilisateur.

Nous avons également annoté notre classe de configuration avec@EnableOAuthSso qui convertit notre application en client OAuth et crée les composants nécessaires pour qu'elle se comporte comme telle.

Bien que Spring crée la plupart des composants pour nous par défaut, nous devons néanmoins configurer certaines propriétés:

security.oauth2.client.client-id=89a7c4facbb3434d599d
security.oauth2.client.client-secret=9b3b08e4a340bd20e866787e4645b54f73d74b6a
security.oauth2.client.access-token-uri=https://github.com/login/oauth/access_token
security.oauth2.client.user-authorization-uri=https://github.com/login/oauth/authorize
security.oauth2.client.scope=read:user,user:email
security.oauth2.resource.user-info-uri=https://api.github.com/user

Au lieu de nous occuper de la gestion des comptes utilisateurs, nous la déléguons à un tiers - dans ce cas, Github - nous permettant ainsi de nous concentrer sur la logique de notre application.

4. Extracteur de Principal et Autorités

Lorsque vous agissez en tant que client OAuth et authentifiez les utilisateurs via un tiers, vous devez prendre en compte trois étapes:

  1. Authentification de l'utilisateur - l'utilisateur s'authentifie avec le tiers

  2. Autorisation de l'utilisateur - après l'authentification, c'est lorsque l'utilisateur autorise notre application à effectuer certaines opérations en son nom; c'est là qu'interviennentscopes

  3. Récupérer les données utilisateur: utilisez le jeton OAuth que nous avons obtenu pour récupérer les données utilisateur

Une fois que nous avons récupéré les données de l'utilisateur,Spring is able to automatically create the user’s Principal and Authorities.

Bien que cela puisse être acceptable, nous nous retrouvons le plus souvent dans un scénario dans lequel nous voulons avoir un contrôle total sur eux.

Pour ce faire,Spring gives us two interfaces we can use to override its default behavior:

  • PrincipalExtractor - Interface que nous pouvons utiliser pour fournir notre logique personnalisée pour extraire lesPrincipal

  • AuthoritiesExtractor - Similaire àPrincipalExtractor, mais il est utilisé pour personnaliser l'extraction deAuthorities à la place

Par défaut,Spring provides two components – FixedPrincipalExtractor and FixedAuthoritiesExtractor  qui implémentent ces interfaces et ont une stratégie prédéfinie pour les créer pour nous.

4.1. Personnalisation de l'authentification de Github

Dans notre cas, nous savons à quoi ressemblent les données utilisateur de Githublike et ce que nous pouvons utiliser pour les personnaliser en fonction de nos besoins.

En tant que tel, pour remplacer les composants par défaut de Spring, nous devons simplement créer deuxBeans qui implémentent également ces interfaces.

Pour lesPrincipal de notre application, nous allons simplement utiliser le nom d'utilisateur Github de l'utilisateur:

public class GithubPrincipalExtractor
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map map) {
        return map.get("login");
    }
}

En fonction de l'abonnement Github de nos utilisateurs - gratuit ou autre - nous leur donnerons une autoritéGITHUB_USER_SUBSCRIBED ouGITHUB_USER_FREE:

public class GithubAuthoritiesExtractor
  implements AuthoritiesExtractor {
    List GITHUB_FREE_AUTHORITIES
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_FREE");
    List GITHUB_SUBSCRIBED_AUTHORITIES
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_SUBSCRIBED");

    @Override
    public List extractAuthorities
      (Map map) {

        if (Objects.nonNull(map.get("plan"))) {
            if (!((LinkedHashMap) map.get("plan"))
              .get("name")
              .equals("free")) {
                return GITHUB_SUBSCRIBED_AUTHORITIES;
            }
        }
        return GITHUB_FREE_AUTHORITIES;
    }
}

Ensuite, nous devons également créer des beans en utilisant ces classes:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor githubPrincipalExtractor() {
        return new GithubPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor githubAuthoritiesExtractor() {
        return new GithubAuthoritiesExtractor();
    }
}

4.2. Utilisation d'un serveur d'autorisation personnalisé

Nous pouvons également utiliser notre propre serveur d'autorisations pour nos utilisateurs, au lieu de faire appel à un tiers.

Malgré le serveur d'autorisation que nous décidons d'utiliser, les composants dont nous avons besoin pour personnaliser à la foisPrincipal etAuthorities restent les mêmes: unPrincipalExtractor et unAuthoritiesExtractor.

Nous avons juste besoin debe aware of the data returned by the user-info-uri endpoint et de l'utiliser comme bon nous semble.

Modifions notre application pour authentifier nos utilisateurs à l'aide du serveur d'autorisation décrit dans l'article dethis:

security.oauth2.client.client-id=SampleClientId
security.oauth2.client.client-secret=secret
security.oauth2.client.access-token-uri=http://localhost:8081/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8081/auth/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:8081/auth/user/me

Maintenant que nous pointons vers notre serveur d'autorisation, nous devons créer les deux extracteurs; dans ce cas, notrePrincipalExtractor va extraire lesPrincipal desMap en utilisant la cléname:

public class examplePrincipalExtractor
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map map) {
        return map.get("name");
    }
}

En ce qui concerne les autorités, notre serveur d’autorisation les place déjà dans ses données deuser-info-uri.

À ce titre, nous allons les extraire et les enrichir:

public class exampleAuthoritiesExtractor
  implements AuthoritiesExtractor {

    @Override
    public List extractAuthorities
      (Map map) {
        return AuthorityUtils
          .commaSeparatedStringToAuthorityList(asAuthorities(map));
    }

    private String asAuthorities(Map map) {
        List authorities = new ArrayList<>();
        authorities.add("example_USER");
        List> authz =
          (List>) map.get("authorities");
        for (LinkedHashMap entry : authz) {
            authorities.add(entry.get("authority"));
        }
        return String.join(",", authorities);
    }
}

Ensuite, nous ajouterons les beans à notre classeSecurityConfig:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor examplePrincipalExtractor() {
        return new examplePrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor exampleAuthoritiesExtractor() {
        return new exampleAuthoritiesExtractor();
    }
}

5. Conclusion

Dans cet article, nous avons mis en œuvre une application qui délègue l'authentification des utilisateurs à un tiers, ainsi qu'à un serveur d'autorisation personnalisé, et nous avons montré comment personnaliser à la foisPrincipal etAuthorities.

Comme d'habitude, l'implémentation de cet exemple peut être trouvéeover on Github.

Lors de l'exécution locale, vous pouvez exécuter et tester l'application àlocalhost:8082