Mon cas d'utilisation est que j'utilise la connexion Oauth2 de Spring Security 5.2, mais j'aimerais que ma classe d'utilisateurs de base de données soit disponible aux côtés de Oauth2AuthenticationToken dans l'authentification. C'est pour que ma classe d'utilisateurs de base de données soit mise en cache par le SecurityContextHolder.

En pseudocode:

  1. Un utilisateur se connecte en utilisant Google ou Github Oauth2
  2. Mon application trouve (ou crée) l'utilisateur de la base de données avec les informations renvoyées
  3. Mon application enregistre dans le SecurityContextHolder un wrapper d'authentification personnalisé qui encapsule à la fois Oauth2AuthenticationToken et la classe User de la base de données
  4. Sur les demandes suivantes, le wrapper d'authentification personnalisé est disponible pour les méthodes du contrôleur

Voici mes tentatives de wrapper :

class MyAuthenticationWrapper implements Authentication {

        public MyAuthenticationWrapper(User user, Authentication underlyingAuth1) {
            this.user = user;
            this.underlyingAuth = underlyingAuth1;
        }

        private final User user;
        private final Authentication underlyingAuth;

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return underlyingAuth.getAuthorities();
        }

        @Override
        public void setAuthenticated(boolean isAuthenticated) {
            underlyingAuth.setAuthenticated(isAuthenticated);
        }

        @Override
        public String getName() {
            return underlyingAuth.getName();
        }

        @Override
        public Object getCredentials() {
            return underlyingAuth.getCredentials();
        }

        @Override
        public Object getPrincipal() {
            return underlyingAuth.getPrincipal();
        }

        @Override
        public boolean isAuthenticated() {
            return underlyingAuth.isAuthenticated();
        }

        @Override
        public Object getDetails() {
            return underlyingAuth.getDetails();
        }

    public User getUser() {
        return user;
    }
}

Et un Oauth2AuthenticationFilter personnalisé :

@Component
class CustomLoginAuthenticationFilter extends OAuth2LoginAuthenticationFilter {

    @Autowired
    private UserDAO userDAO;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    Authentication auth = super.attemptAuthentication(request, response);
    if (auth instanceof OAuth2AuthenticationToken) {
        switch (((OAuth2AuthenticationToken) auth).authorizedClientRegistrationId) {
            case "google":
                Optional<User> user = userDAO.findByEmail(username);
                if (!user.isPresent()) {
                    throw new NotFoundException("!");
                }
                return MyAuthenticationWrapper(auth, user.get());
            }
        }
        return auth;
    }
}

Je n'ai pas réussi à faire fonctionner cette approche, et je me demande si c'est vraiment la bonne approche.

Existe-t-il une autre approche plus idiomatique pour combiner les données utilisateur de la base de données avec les données utilisateur Oauth2 dans la sécurité Spring ?

0
Nate Vaughan 29 janv. 2020 à 10:34

1 réponse

Meilleure réponse

Peut-être qu'un examen de OAuth2UserService pourrait vous aider. Il est invoqué après avoir obtenu avec succès le jeton OAuth. Voici comment cela fonctionnerait :

  1. Un utilisateur se connecte à l'aide de Google ou de Github Oauth2

Pas besoin d'ajouter quoi que ce soit. Laissez les filtres par défaut s'en occuper.

  1. Mon application trouve (ou crée) l'utilisateur de la base de données avec les informations renvoyées

Créez votre propre OAuth2UserService en tant que bean (il sera récupéré automatiquement) qui s'occupera de gérer la base de données :

@Component
public class CustomService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest)
        throws OAuth2AuthenticationException {
        // ... DB logic goes here
    }
}

En loadUser(...), le OAuth2UserRequest vous donne accès au ClientRegistration correspondant et au OAuth2AccessToken, que vous pouvez ensuite utiliser pour interroger ou mettre à jour la base de données.

  1. Mon application enregistre dans le SecurityContextHolder un wrapper d'authentification personnalisé qui encapsule à la fois Oauth2AuthenticationToken et la classe User de la base de données

Pas besoin de s'occuper d'un emballage ! Le OAuth2User personnalisé que vous construisez à partir des informations de la base de données sera le Principal dans le OAuth2LoginAuthenticationToken, qui finit par être le Authentication, il sera donc disponible pour votre application . Puisque vous ne vous occupez pas du Authentication vous-même, vous n'auriez pas à vous soucier de le sauvegarder dans le SecurityContextHolder.

  1. Sur les demandes suivantes, le wrapper d'authentification personnalisé est disponible pour les méthodes du contrôleur

Votre Authentication sera de type OAuth2LoginAuthenticationToken. Vous pouvez obtenir votre OAuth2User personnalisé comme ceci :

OAuth2LoginAuthenticationToken auth = //...
OAuth2User user = auth.getPrincipal();

Pour plus d'informations sur les classes de base que vous traitez, celles-ci peuvent être utiles :

Pour les implémentations prêtes à l'emploi de OAuth2UserService, consultez :

2
NatFar 29 janv. 2020 à 15:51