web-dev-qa-db-fra.com

Spring boot - retourne un objet utilisateur après la connexion

J'ai une application de démarrage de printemps avec WebSecurityConfigurerAdapter configuré comme ceci -

http.csrf().disable()
                    .exceptionHandling()
                    .authenticationEntryPoint(restAuthenticationEntryPoint)
                    .and()
                .authorizeRequests()
                    .antMatchers("/user/*", "/habbit/*").authenticated()
                    .and()
                .formLogin()
                    .loginProcessingUrl("/login")
                    .permitAll()
                    .usernameParameter("email")
                    .passwordParameter("pass")
                    .successHandler(authenticationSuccessHandler)
                    .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                    .and()
                .logout()
                    .logoutUrl("/logout")
                    .invalidateHttpSession(true);

Puis-je ajouter quelque chose comme mon propre contrôleur qui, après une authentification réussie, retournerait un objet personnalisé avec quelques détails sur l'utilisateur authentifié?

pdate: Pour plus de clarté, j'utilise une application angular comme client. Actuellement, je dois faire 2 demandes de mon client au serveur: 1. POST demande/URL de connexion pour l'authentification. 2. OBTENEZ une demande pour récupérer les données utilisateur authentifiées.

Mon objectif est de me renvoyer la 1ère demande d'informations utilisateur afin de ne pas avoir à faire la demande 2dn. Actuellement, la 1ère demande authentifie uniquement l'utilisateur, crée une session sur le serveur et renvoie une réponse d'état '200 OK' avec pas de données. Je veux qu'il renvoie une réponse réussie avec des données sur l'utilisateur connecté.

Répondu:

La bonne réponse est dans les commentaires, je vais donc l'écrire ici: je devais rediriger de mon successHandler vers mon contrôleur qui à son tour retourne les informations utilisateur actuellement connecté (dans mon cas, le contrôleur est dans l'url '/ user/me':

 @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {
        clearAuthenticationAttributes(request);
        getRedirectStrategy().sendRedirect(request, response, "/user/me");
    }
14
Tomas

Si je comprends bien votre problème, je peux suggérer la voie suivante.

Tout d'abord, vous devez implémenter la classe, qui contiendra les informations utilisateur. Cette classe doit être héritée de org.springframework.security.core.userdetails.User:

public class CustomUserDetails extends User {

    public CustomUserDetails(String username, String password,
         Collection<? extends GrantedAuthority> authorities) {            
        super(username, password, authorities);
    }

    //for example lets add some person data        
    private String firstName;
    private String lastName;

    //getters and setters
}

L'étape suivante, vous avez créé votre propre implémentation de l'interface org.springframework.security.core.userdetails.UserDetailsService:

@Service
public class CustomUserDetailService implements UserDetailsService{

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException{         

        if(StringUtils.isEmpty(userName)) 
            throw new UsernameNotFoundException("User name is empty");

        //if you don't use authority based security, just add empty set
        Set<GrantedAuthority> authorities = new HashSet<>();
        CustomUserDetails userDetails = new CustomUserDetails(userName, "", authorities);            

        //here you can load user's data from DB or from 
        //any other source and do:
        //userDetails.setFirstName(firstName);
        //userDetails.setLastName(lastName);

        return userDetails;
    }

}

Comme vous le voyez, cette classe n'a qu'une seule méthode, où vous pouvez charger et définir des détails utilisateur personnalisés. Notez que j'ai marqué cette classe avec @Service annotation. Mais vous pouvez l'enregistrer dans votre contexte Java-config ou XML.

Maintenant, pour accéder à vos données utilisateur après une authentification réussie, vous pouvez utiliser l'approche suivante, lorsque Spring passera automatiquement le principal dans la méthode du contrôleur:

@Controller
public class MyController{

    @RequestMapping("/mapping")
    public String myMethod(Principal principal, ModelMap model){
        CustomUserDetails userDetails = (CustomUserDetails)principal;
        model.addAttribute("firstName", userDetails.getFirstName());
        model.addAttribute("lastName", userDetails.getLastName());
    }
}

Ou une autre façon:

@Controller
public class MyController{

    @RequestMapping("/mapping")
    public String myMethod(ModelMap model){
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        CustomUserDetails userDetails = (CustomUserDetails)auth.getPrincipal();
        model.addAttribute("firstName", userDetails.getFirstName());
        model.addAttribute("lastName", userDetails.getLastName());
    }
}

Cette méthode peut être utilisée dans d'autres endroits, où Spring ne passe pas automatiquement le principal.

Pour accéder à une adresse spécifique après une authentification réussie, vous pouvez utiliser SimpleUrlAuthenticationSuccessHandler. Créez-le simplement dans votre configuration:

@Bean
public SavedRequestAwareAuthenticationSuccessHandler successHandler() {
    SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    successHandler.setTargetUrlParameter("/succeslogin");
    return successHandler;
}

et utilisez-le dans votre configuration:

http.formLogin()
    .loginProcessingUrl("/login")
    .permitAll()
    .usernameParameter("email")
    .passwordParameter("pass")
    .successHandler(successHandler())

après cela, vous pouvez créer un contrôleur, qui enverra une réponse à partir d'une URL spécifiée:

@Controller
@RequestMapping("/sucesslogin")
public class SuccessLoginController{

     @RequestMapping(method = RequestMethod.POST)
     public String index(ModelMap model, Principal principal){
         //here you can return view with response
     }

}

Bien entendu, vous pouvez renvoyer non seulement la vue, mais la réponse JSON (en utilisant @ResponseBody annotation), ou autre chose, dépend de votre front-end. J'espère que cela vous sera utile.

13
Ken Bekov

Dans la réponse acceptée, vous avez besoin de deux appels pour obtenir les données souhaitées. Renvoyez simplement les données après la connexion dans un AjaxAuthenticationSuccessHandler personnalisé comme celui-ci.

@Bean
public AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() {
    return new AjaxAuthenticationSuccessHandler() {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            response.getWriter().write(new ObjectMapper().writeValueAsString(new UserAuthenticationResponse(authentication.getName(), 123l)));
            response.setStatus(200);
        }

    };
}

et enregistrez le gestionnaire de réussite:

http.successHandler(ajaxAuthenticationSuccessHandler())
6
Simon Ludwig