I am using spring security for authentication and successfully able to get User
object (org.springframework.security.core.userdetails.User
) anywhere I need.
But I want UserId
also, which is not there in spring's User
object. So I create my own object(com.app.site.pojo.User
). It has few additional variables like userId,user date of birth etc. Now I want spring security to use my user
object instead of spring User
object. and it should have all the details about that user.
I tried to type cast the user object to my object, it is throwing exception (It is obvious).
I dont want to make DB call again to get the userId from DB again.
How can I achieve it?
A better way of doing this is to create a UserDetaisService that returns an object that extends your type of Object and implements UserDetails. This means you only need to perform one database lookup.
By having the result of loadUserByUsername implement UserDetails and extend your User object, you can refer to it as the custom user object and Spring Security can refer to it as a UserDetails.
For example:
@Service
public class UserRepositoryUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserRepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
return new UserRepositoryUserDetails(user);
}
private final static class UserRepositoryUserDetails extends User implements UserDetails {
private UserRepositoryUserDetails(User user) {
super(user);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
@Override
public String getUsername() {
return getEmail();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
private static final long serialVersionUID = 5639683223516504866L;
}
}
You can then refer to the custom UserDetailsService in your configuration. For example:
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetails" />
</security:authentication-manager>
<bean id="customUserDetails" class="sample.UserRepositoryUserDetailsService"/>
If you are using Spring Security 3.2.x+ you can use Spring Security's @AuthenticationPrincipal to resolve the custom user object. For example:
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {
// .. find messags for this user and return them ...
}
Otherwise you can use the following:
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(Authentication auth) {
User customUser = (User) auth.getPrincipal();
...
}
If using the XML configuration, you will need to register AuthenticationPrincipalArgumentResolver with Spring MVC.
<mvc:annotation-driven>
<mvc:argument-resolvers>
<beans:bean class="org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
You can find details about this in my github sample project. The README also reference the recorded webinar.
I was in the same situation as you, what I did was redirect the user to a new page after login, and create a controller function of that page, to get the user from DB and store his id as a Session Variable.
@RequestMapping(value = { "/overview" }, method = RequestMethod.GET)
public ModelAndView overViewPage(HttpServletRequest request) {
ModelAndView model = new ModelAndView();
model.addObject("title", "Spring Security + Hibernate Example");
model.addObject("message", "This is default page!");
model.setViewName("hello");
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UserDetails userDetail = (UserDetails) auth.getPrincipal();
User u = userService.getUser(userDetail.getUsername());
request.getSession().setAttribute("userId", u.getId());
return model;
}
You can use the user object or just use his id for future queries by doing
int userId = (int) request.getSession().getAttribute("userId");
My userService is just a simple service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.sports.dao.UserDao;
@Service
@Transactional
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
public com.sports.models.User getUser(String username){
return userDao.findByUserName(username);
}
}
I'm also new to spring so I'm not sure if this is the best way to do it.
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