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.
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.
@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
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
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>
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.
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.