Reputation: 792
I know you can get a username easily in a Spring controller by including Principal
as a method argument like:
@GetMapping("/username")
@ResponseBody
public String currentUserName(Principal principal) {
return principal.getName();
}
But I am ultimately going to want access to members of a MyCustomUser
class that I instantiate from a repository with a findBy
method. I can put a helper method in the Controller to do the lookup and return the user based on principal.getName()
, but can I go a step further and bind to MyCustomUser
directly, like
@GetMapping("/stuff")
@ResponseBody
public String stuff(MyCustomUser user) {
return user.thing();
}
I was looking into creating a converter like (Ref):
@Component
public class PrincipalToMyCustomUserConverter implements Converter<Principal, MyCustomUser> {
private MyCustomUserRepository userRepository;
public PrincipalToApplicationUserConverter(MyCustomUserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public MyCustomUser convert(Principal source) {
return this.userRepository.findByUsername(source.getName());
}
}
But I don't know if that's an appropriate way to grab the repository, and I don't know how to pass the repository when registering the converter (Ref).
Upvotes: 0
Views: 143
Reputation: 5862
You're correct in that the converter you're proposing is not appropriate. Your converter can convert from an object of type Principal
to an object of type MyCustomUser
, however, there is no *Principal*
by which to convert. The magic behind the principal injection is that Spring actually gets this from the SecurityContextHolder
, it is not deserialized from request...though fields present in the request allow Spring to create the Principal
. If you truly want to inject MyCustomUser
, use a ModelAttribute
. ModelAttributes
are available to all of your Spring controller methods.
I generally like to keep stuff like this in it's own class, so I would define a class that held this and other @ControllerAdvice in one place, something like this:
@ControllerAdvice
public class SomeControllerAdvice {
@Autowired
private MyCustomUserRepository myCustomUserRepository;
@ModelAttribute
public MyCustomUser getUser(Principal principal) {
return myCustomUserRepository.findByUsername(principal.getName());
}
}
The above should suffice to make MyCustomUser available to all methods. I would note that you probably want a little error handling here, like skip over if principal is null and whatnot, also have your findByUsername
method return an Optional so your can address empty returns.
Upvotes: 1