web-dev-qa-db-fra.com

Comment mapper un jeton OAuth 2 sur un objet UserDetails dans un serveur de ressources?

J'ai 2 applications Spring Boot distinctes, l'une servant de serveur d'autorisation OAuth 2 et l'autre de serveur de ressources. J'utilise la variable RemoteTokenServices de Spring dans mon serveur de ressources pour vérifier les jetons du serveur d'autorisation. À présent, j'essaie de définir un code de contrôleur protégé dans mon application de serveur de ressources, mais je ne sais pas comment mapper la classe UserDetails sur le principal d'authentification fourni via le mécanisme OAuth 2.

J'ai configuré mon serveur d'autorisation avec une variable TokenEnhancer personnalisée qui ajoute plus de détails au jeton, de telle sorte que /oauth/check_token?token=<token> renvoie des champs personnalisés que je souhaite mapper sur les contrôleurs de mon serveur de ressources.

Dans une configuration plus monolithique où le serveur d'autorisation est également le serveur de ressources, je peux définir les méthodes du contrôleur qui utilisent le principal authentifié de la manière suivante:

//User implements UserDetails
public Map<String, Object> getResource(@AuthenticationPrincipal User user) {
    //code that uses the user object
}

Cependant, cela ne semble pas fonctionner aussi facilement dans une approche plus distribuée. Le mappage échoue et le paramètre user finit par être un objet null. J'ai essayé d'utiliser l'approche suivante:

public Map<String, Object> getResource(Authentication authentication) {
    //code that uses the authentication object
}

Bien que le code ci-dessus mappe correctement les informations d'authentification, il ne me permet pas d'accéder directement aux champs personnalisés que j'ai définis via la variable TokenEnhancer dont j'ai parlé plus tôt. Je n'arrive pas à trouver quoi que ce soit dans la documentation du printemps à ce sujet.

9
Psycho Punch

Pour résoudre le problème, permettez-moi d'abord de passer en revue un peu d'arrière-plan architectural. L'objet UserDetails automatiquement mappé via le @AuthenticationPrincipal provient du champ principal de l'objet Authentication actif. Un contrôleur de serveur de ressources a accès à un objet OAuth2Authencation, qui est une instance spécialisée de Authentication pour la structure de sécurité Spring OAuth2, simplement en la déclarant dans les paramètres de méthode. 

public void controllerMethod(OAuth2Authentication authentication) {
    //controller definition
}

Sachant cela, le problème consiste maintenant à savoir comment s'assurer que la méthode getPrincipal() dans l'objet Authentication est une instance de ma classe UserDetails personnalisée. La RemoteTokenServices que j'utilise dans l'application du serveur de ressources utilise une instance de AccessTokenConverter pour interpréter les détails des jetons envoyés par le serveur d'autorisation. Par défaut, il utilise DefaultAccessTokenConverter, qui définit simplement le principal d'authentification en tant que nom d'utilisateur, qui est un String. Ce convertisseur utilise UserAuthenticationConverter pour convertir les données provenant du serveur d'autorisation en une instance de Authentication. Voici ce dont j'avais besoin pour personnaliser:

DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {

    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        Authentication authentication = super.extractAuthentication(map);
        // User is my custom UserDetails class
        User user = new User();
        user.setSpecialKey(map.get("specialKey").toString());
        return new UsernamePasswordAuthenticationToken(user,
                authentication.getCredentials(), authentication.getAuthorities());
    }

});
tokenServices.setAccessTokenConverter(tokenConverter);

Avec toutes ces configurations, le mécanisme @AuthenticationPrincipal fonctionne maintenant comme prévu.

15
Psycho Punch

Avez-vous activé la AuthenticationPrincipalArgumentResolver, comme après le xml?

<mvc:annotation-driven>
        <mvc:argument-resolvers>
                <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
        </mvc:argument-resolvers>
</mvc:annotation-driven>

Et vous devez implémenter la UserDetails pour renvoyer votre propre CustomerUser object. Vous pouvez ensuite utiliser l'annotation pour obtenir le principal directement.

0
chaoluo