spring security get user id from DB

11,370

Solution 1

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.

Solution 2

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.

Share:
11,370
user3035305
Author by

user3035305

Updated on June 04, 2022

Comments

  • user3035305
    user3035305 almost 2 years

    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?