Spring Security - Adding more information to the authenticated user.

I was using spring Security to perform authentication against the database and I needed to load the user information once the user gets authenticated. However, the spring org.springframework.security.core.userdetails.User object was not allowing me add more details to the logged in User object. In my case, I had to add phone number to the authenticated user. In the following section I'll show as to how to add additional information to the authenticated user.

Create an object to contain the additional information.

We will first create a MediUser object which will extend the Spring org.springframework.security.core.userdetails.User object. The MediUser object contains additional information like first name, middle name, last name, phone number and alternative phone number. Following is the code for MediUser:

  
  import java.util.Collection;
  import org.springframework.security.core.GrantedAuthority;
  import org.springframework.security.core.userdetails.User;

  public class MediUser extends User {

     private static final long serialVersionUID = -3531439484732724601L;

     private final String firstName;
     private final String middleName;
     private final String lastName;
     private final long phNumber;
     private final long altPhNumber;

     public MediUser(String username, String password, boolean enabled,
         boolean accountNonExpired, boolean credentialsNonExpired,
         boolean accountNonLocked,
         Collection authorities,
         String firstName, String middleName, String lastName,
         long phNumber, long altPhNumber) {

             super(username, password, enabled, accountNonExpired,
                credentialsNonExpired, accountNonLocked, authorities);

             this.firstName = firstName;
             this.middleName = middleName;
             this.lastName = lastName;
             this.phNumber = phNumber;
             this.altPhNumber = altPhNumber;
     }

     public static long getSerialversionuid() {
        return serialVersionUID;
     }

     public String getFirstName() {
        return firstName;
     }

     public String getMiddleName() {
        return middleName;
     }

     public String getLastName() {
        return lastName;
     }

     public long getPhNumber() {
       return phNumber;
     }

     public long getAltPhNumber() {
       return altPhNumber;
     }
  
  }

  
 

Use MediUser object into UserDetailsService.

Now we will use the MediUser object in the UserDetailsService implementation class instead of org.springframework.security.core.userdetails.User. Following is the code snippet for UserDetailServiceImpl :

  
  import java.util.ArrayList;
  import java.util.List;

  import org.springframework.dao.DataAccessException;
  import org.springframework.security.core.GrantedAuthority;
  import org.springframework.security.core.authority.GrantedAuthorityImpl;
  import org.springframework.security.core.userdetails.UserDetails;
  import org.springframework.security.core.userdetails.UserDetailsService;  
  import org.springframework.security.core.userdetails.UsernameNotFoundException;
  import org.springframework.stereotype.Component;

  import com.rabbit.report.login.beans.MediUser;
  import com.rabbit.report.login.beans.UserProfile;
  import com.rabbit.report.login.beans.UserRoles;

  @Component
  public class UserDetailServiceImpl implements UserDetailsService {

      LogInService logInService;

      public UserDetails loadUserByUsername(String username)
               throws UsernameNotFoundException, DataAccessException {
          MediUser mediUser = null;
          List authorities = new ArrayList();
   
          if (!"".equals(username)) {
               UserProfile userProfile = logInService.loadUserDetails(username);
             
               if (null != userProfile) {
                    List userRoles = userProfile.getUserRoles();

                    if (null != userRoles && userRoles.size() > 0) {
                         for (UserRoles userRole : userRoles) {
                               authorities.add(new GrantedAuthorityImpl(userRole.getRoleName()));
                    }

                    mediUser = new MediUser(username,
                    userProfile.getPassword(), true, true, true, true,authorities, 
                       userProfile.getFirstName(),userProfile.getMiddleName(),
                       userProfile.getLastName(), userProfile.getPhNumber(), userProfile.getAltPhNumber());

              } else {
                      mediUser = new MediUser(username, "NA", true, 
                          true, true, true, authorities, null, null, null, 0, 0);
              }          
          } else {
                 mediUser = new MediUser(username, "NA", true, 
                    true, true, true, authorities, null, null, null, 0, 0);
          }

       logInService.closeSession();
       return mediUser;
     }

     public LogInService getLogInService() {
         return logInService;
     }

     public void setLogInService(LogInService logInService) {
         this.logInService = logInService;
     }

  }

   
 

Get the MediUser object in the Controller.

In the controller, we will first retrieve the Autentication object from SecuirtyContextHolder and then Pricipal object form Authentication object. After that we will cast the Principal object into MediUser object. Once we have the MediUser object, we can use it to extract the additional information. In the following code snippet we are extracting the phone number from the MediUser object:
  

  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;

  import org.springframework.security.core.Authentication;
  import org.springframework.security.core.context.SecurityContextHolder;
  import org.springframework.stereotype.Controller;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RequestMethod;
  import org.springframework.web.servlet.ModelAndView;

  import com.rabbit.report.login.beans.MediUser;

  @Controller
  public class LoginController {

   @RequestMapping(value = "/loginSuccess", method = RequestMethod.GET)
   public ModelAndView loginSuccess(HttpServletRequest request,
     HttpServletResponse response) throws Exception {
  
     Authentication auth = SecurityContextHolder.getContext().getAuthentication();
     MediUser currentUser = (MediUser)auth.getPrincipal();
     System.out.println("Phone Number : " + currentUser.getPhoneNumber());
                .........................
                .........................
                .........................
  
     ModelAndView modelAndView = new ModelAndView("myReports");

     return modelAndView;
   }
  }
  
 

Comments

  1. Hi Yogender,
    Could you also post the implementation of loadUserDetails(username) of LoginService class. I want to know how to handle failed authentication requests.
    Thanks in advance.
    Regards

    ReplyDelete
    Replies
    1. You can handle failed authentication from spring-security.xml, using authentication-failure-url. Following is the code snippet of spring-security.xml.

      form-login login-processing-url="/login" login-page="/restLogin" authentication-success-handler-ref="authenticationHandler" authentication-failure-url="/loginFailure"

      In your controller, you can trap this url and do whatever you want to do. In my case I'm forwarding the user to a loginFailure page. Following is code snippet of LoginController for handling "/loginFailure":

      @Controller
      public class LoginController {

      @RequestMapping(value = "/loginFailure")
      public ModelAndView _handleRequest(HttpServletRequest request,
      HttpServletResponse response) throws Exception {
      System.out.println("inside loginFailure .......");
      ModelAndView modelAndView = new ModelAndView("loginFailure");
      return modelAndView;
      }
      ..............................
      ..............................
      }

      Delete
  2. Do you have any idea why my system throws the cast class exception
    while **getPrincipal() is being cast to CustomUser in ** Controller?
    I'm pretty sure I followed almost all of the options you did.
    Or should I add something like '' ?
    If you are not busy and don't mind, please let me know, or email me to subong6987@naver.com

    Thank you

    ReplyDelete
    Replies
    1. Sorry for looking at it very late.

      The only reason, I see for this error is that your CustomerUser has not extended the org.springframework.security.core.userdetails.User.

      If this is not true then it can be tested by debugging the code.

      Delete
    2. or you are not explicitly casting it to your CustomerUser. Like

      CutomerUser customerUser = (CustomerUser)auth.getPrincipal();

      Delete
  3. something like 'security:authentication property="principal.firstname" ' in security.xml
    '

    ReplyDelete
  4. Where I can download your project? :D

    ReplyDelete
  5. I haven't uploaded it to any repo....

    ReplyDelete
  6. Hi, thanks for the article. But I wonder if it is possible to put the mediUser object in one service and get the object in another service.

    ReplyDelete
  7. Alcohol became something that I couldn’t control despite how miserable it was making me. more information

    ReplyDelete

Post a Comment

Popular posts from this blog

Hibernate Difference between update() and merge()

Running tomcat maven plugin in Debug mode (in STS, Eclipse and MyEclipse)