web-dev-qa-db-fra.com

Spring Security @AuthenticationPrincipal

J'ai essayé de faire en sorte que @AuthenticationPrincipal fonctionne correctement avec une classe d'utilisateur personnalisée. Malheureusement, l'utilisateur est toujours nul. Voici le code:

Manette

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(@AuthenticationPrincipal User user) {
    ModelAndView mav= new ModelAndView("/web/index");
    mav.addObject("user", user);
    return mav;
}

Config de sécurité

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomUserDetailsService customUserDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
    }

}

CustomUserDetailsService

@Component
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // Spring Data findByXY function
    return userRepository.findByUsername(username);
}

Entité utilisateur

public class User implements UserDetails{
    private String username;
    private String password;
    private Collection<Authority> authorities;

    // Getters and Setters

}

Autorité

public class Authority implements GrantedAuthority{
    private User user;
    private String role;

    // Getters and Setters

    @Override
    public String getAuthority() {
        return this.getRole();
    }
}

J'ai essayé diverses solutions à ce problème que j'ai trouvées en ligne, par exemple. convertir mon objet utilisateur personnalisé comme ceci:

return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true, true, true, true,  authorities);

Les autres moyens d'obtenir les utilisateurs actifs fonctionnent sans problème, mais je trouve que @AuthenticationProvider CustomUserObject est le moyen le plus propre. C'est pourquoi j'aimerais que cela fonctionne. Toute aide est grandement appréciée.

9
Lukehey

Au lieu d'utiliser @AuthenticationPrincipal, vous pouvez directement spécifier votre dépendance pour l'utilisateur authentifié dans l'argument de méthode. quelque chose comme donné ci-dessous

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(Principal user) {
    ModelAndView mav= new ModelAndView("/web/index");
    mav.addObject("user", user);
    return mav;
} 

Cet objet principal sera un objet réel authentifié par la sécurité de printemps. Spring l'injectera pour vous lorsque la méthode sera invoquée.

4
Imrank
@RequestMapping(method= RequestMethod.GET,value="/authenticate", produces = MediaType.APPLICATION_JSON_VALUE)
public Object authenticate(@AuthenticationPrincipal Object obj) {
    return obj;
}

J'ai le même problème, j'ai pu faire ce travail. Vous pouvez l'utiliser avec un mappeur

@RequestMapping(method = RequestMethod.GET, value = "/authenticate2", produces = MediaType.APPLICATION_JSON_VALUE)
public User authenticate2(@AuthenticationPrincipal Object obj) throws IOException {
    return mapper.readValue(mapper.writeValueAsString(obj), User.class);
}

Ceux-ci ont fonctionné pour moi, j'espère que cela fonctionnera pour n'importe qui à l'avenir

1
Ali Miskeen

Dans mon cas, je reçois une String retour (le nom d'utilisateur) et non l'objet UserDetails, c'est-à-dire que vous devez définir la signature de la méthode comme suit: 

public ModelAndView index(@AuthenticationPrincipal String username)

Ce n'est pas étrange, puisque @AuthenticationPrincipal renvoie en effet Authentication.getPrincipal() et selon la documentation:

Dans le cas d'une demande d'authentification avec nom d'utilisateur et mot de passe, , Il s'agirait du nom d'utilisateur. Les appelants sont censés renseigner le principal Pour une demande d'authentification.

L’implémentation AuthenticationManager renvoie souvent une authentification Contenant des informations plus riches en tant que principal à utiliser par Par l’application. De nombreux fournisseurs d'authentification créeront un objet UserDetails comme principal. Voir: https://docs.spring.io/spring-security/site/docs/5.0.0. RELEASE/api/

Donc, je suppose que votre implémentation AuthenticationManager renvoie uniquement un nom d'utilisateur

1
user2465039

Je suis confronté exactement au même problème que vous. Pour une raison quelconque, @EnableWebSecurity de Spring Security n'ajoute pas argumentResolver automatiquement, vous devez l'ajouter manuellement:

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

Vous devez vérifier si vous utilisez la version correcte de l'annotation @AuthenticationPrincipal et la version correcte de la classe AuthenticationPrincipalArgumentResolver ensemble.

Avant la version 4.0 de Spring Security, vous deviez utiliser des classes: 

  • org.springframework.security.web.bind.annotation.AuthenticationPrincipal

  • org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver

A partir de la version 4.0, vous devez utiliser:

  • org.springframework.security.core.annotation.AuthenticationPrincipal

  • org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver

Regardez @AuthenticationPrincipal docs officiels pour des exemples de configuration.

0
Matias Sebastiao

J'ai trouvé une autre solution, même celle-ci n'est pas canonique.

Dans votre contrôleur

@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserDTO> login(@RequestBody UserDTO user){
    try {
        usersService.authenticate(user.getUsername(), user.getPassword());
        return new ResponseEntity<UserDTO>(user, HttpStatus.OK);
    }
    catch (BadCredentialsException e){
        return new ResponseEntity<UserDTO>(HttpStatus.UNAUTHORIZED);
    }
}

Où UserDTO est simplement un formulaire à qui contient un nom d'utilisateur et un mot de passe

Dans votre CustomUserDetailsService

public void authenticate(String username, String password){
    try{
        User user = new User(username, password);
        Authentication request = new UsernamePasswordAuthenticationToken(user, password, Arrays.asList(WebSecurityConfiguration.USER));
        Authentication result = authenticationManager.authenticate(request);
        SecurityContextHolder.getContext().setAuthentication(result);       
    } catch (InternalAuthenticationServiceException e){
        // treat as a bad credential
    }
}

Implémentez votre propre AuthenticationManager

@Component
class DefaultAuthenticationManager implements AuthenticationManager {

@Autowired
private CustomUserDetailsService usersService;

public Authentication authenticate(Authentication auth) throws AuthenticationException {
    UserDetails user = usersService.loadUserByUsername(((User)auth.getPrincipal()).getUsername());
    if (user != null) {
        return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
    }
// Handle bad credentials here  
}
}

Ce qui est fondamental, c’est que la variable Principal du CustomUserDetailsService#authenticate soit un objet, et non le nom de votre utilisateur authentifié, de sorte que la structure puisse le gérer, puis injecter par le biais du mécanisme @AuthenticationPrincipal. Cela a fonctionné pour moi.

0
Rémi Doolaeghe