I've been trying to get @AuthenticationPrincipal to work properly with a custom User class. Unfortunately, the user is always null. Here's the code:
Controller
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(@AuthenticationPrincipal User user) {
ModelAndView mav= new ModelAndView("/web/index");
mav.addObject("user", user);
return mav;
}
Security Config
@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);
}
User Entity
public class User implements UserDetails{
private String username;
private String password;
private Collection<Authority> authorities;
// Getters and Setters
}
Authority Entity
public class Authority implements GrantedAuthority{
private User user;
private String role;
// Getters and Setters
@Override
public String getAuthority() {
return this.getRole();
}
}
I've tried various solutions to this I found online, e.g. converting my custom user object like this:
return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true, true, true, true, authorities);
The other ways to get the active users are working without a problem, but I find the @AuthenticationProvider CustomUserObject to be the cleanest way, which is why I would like to get this to work. Any help is greatly appreciated.
Annotation Type AuthenticationPrincipal Use AuthenticationPrincipal instead. Annotation that binds a method parameter or method return value to the Authentication. getPrincipal() . This is necessary to signal that the argument should be resolved to the current user rather than a user that might be edited on a form.
The HttpServletRequest.getUserPrincipal() will return the result of SecurityContextHolder.getContext().getAuthentication() . This means it is an Authentication which is typically an instance of UsernamePasswordAuthenticationToken when using username and password based authentication.
Instead of using @AuthenticationPrincipal you can directly specify your dependency for authenticated user in method argument. something as given below
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(Principal user) {
ModelAndView mav= new ModelAndView("/web/index");
mav.addObject("user", user);
return mav;
}
This Principal object will be actual object that got authenticated through spring security. Spring will inject this for you when the method will get invoked.
In my case, I get a String
back (the username) and not the UserDetails
object, i.e. you should define the method signature as
public ModelAndView index(@AuthenticationPrincipal String username)
This is not strange, since @AuthenticationPrincipal
in effect returns Authentication.getPrincipal()
and according to the documentation:
In the case of an authentication request with username and password, this would be the username. Callers are expected to populate the principal for an authentication request.
The AuthenticationManager implementation will often return an Authentication containing richer information as the principal for use by the application. Many of the authentication providers will create a UserDetails object as the principal. See: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/api/
So, I am assuming that your AuthenticationManager
implementation is returning just a username
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With