How to get the current logged in user object from spring security?
Asked Answered
S

13

59

I am using Spring security version 3.1.4.RELEASE. How can I access the current logged in user object?

SecurityContextHolder.getContext().getAuthentication().getPrincipal()

returns user name, not user object. So how can I use the returned Username and get the UserDetails object?

I have tried the following code:

public UserDetails getLoggedInUser(){

    final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken))
    {
        if(auth.getDetails() !=null)
            System.out.println(auth.getDetails().getClass());
        if( auth.getDetails() instanceof UserDetails)
        {
            System.out.println("UserDetails");
        }
        else
        {
            System.out.println("!UserDetails");
        }
    }
    return null;
}

Following is the result:

[2015-08-17 19:44:46.738] INFO  http-bio-8443-exec-423   System.out    class org.springframework.security.web.authentication.WebAuthenticationDetails 
[2015-08-17 19:44:46.738] INFO  http-bio-8443-exec-423   System.out    !UserDetails

AuthenticationFilter class as follows:

public class CustomUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
    public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;

    public CustomUsernamePasswordAuthenticationFilter() {
        super("/j_spring_security_check");
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);
        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }
        username = username.trim();
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        if(this.getAuthenticationManager()==null){
            logger.info("Authentication manager is null.");
        } else {
            logger.info("Authentication manager was "+this.getAuthenticationManager().getClass().getName()); 
        }
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(passwordParameter);
    }

    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(usernameParameter);
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setUsernameParameter(String usernameParameter) {
        this.usernameParameter = usernameParameter;
    }

    public void setPasswordParameter(String passwordParameter) {
        this.passwordParameter = passwordParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getUsernameParameter() {
        return usernameParameter;
    }

    public final String getPasswordParameter() {
        return passwordParameter;
    }
}

AuthenticationProvider as follows:

@Component
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    private MyUserDetailsService userDetailsService;

    public MyUserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(MyUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails arg0,
            UsernamePasswordAuthenticationToken arg1)
            throws AuthenticationException {

    }

    @Override
    protected UserDetails retrieveUser(String arg0,
            UsernamePasswordAuthenticationToken arg1)
            throws AuthenticationException {
        return userDetailsService.loadUserByUsername(arg0);
    }
}

UserDetails class as follows:

    public class MyUserDetailsService implements UserDetailsService {       
    private final Map<String, UserDetails> usersList;
    
    public MyUserDetailsService() {
        Collection<GrantedAuthority> authorityList;
        final SimpleGrantedAuthority supervisorAuthority = new SimpleGrantedAuthority("supervisor");
        final SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority("user");
        usersList = new TreeMap<String, UserDetails>();

        authorityList = new ArrayList<GrantedAuthority>();
        authorityList.add(supervisorAuthority);
        authorityList.add(userAuthority);
        usersList.put("admin", new User("admin", "admin", authorityList));

        authorityList = new ArrayList<GrantedAuthority>();
        authorityList.add(userAuthority);
        usersList.put("peter", new User("peter", "password123", authorityList));

        //probably don't use this in production
        for(Map.Entry<String, UserDetails> user : usersList.entrySet()){
            logger.info(user.getValue().toString());
        }
    }

    @Override
    public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {
        UserDetails ud = usersList.get(username);
        if (ud != null) {
            logger.info("loadUserByUsername: found match, returning "
                    + ud.getUsername() + ":" + ud.getPassword() + ":"
                    + ud.getAuthorities().toString());
            return new User(ud.getUsername(), ud.getPassword(),
                    ud.getAuthorities());
        }

        logger.info("loadUserByUsername: did not find match, throwing UsernameNotFoundException");
        throw new UsernameNotFoundException(username);
    }
}
Statuette answered 17/8, 2015 at 13:46 Comment(5)
How do you authenticate your users? What is the AuthenticationProvider, and what is the Filter?Severn
Ok, you a custom AuthenticationFilter not far from a UsernamePasswordAuthenticationFilter. It is common to use a DaoAuthenticationProvider with that. Did you configure setForcePrincipalAsString(True) (or set forcePrincipalAsString to true) anywhere?Severn
@SergeBallesta No I didn't use those methodsStatuette
With the shown code and a default (or common) configuration, SecurityContextHolder.getContext().getAuthentication().getPrincipal() should return the User object provided by MyUserDetailsService. You should try to use a debugger to follow a full authentication request (after downloading sources for SpringSecurity)Severn
Or do you have anything (a filter or ?) that would use the authentication object to set the request Principal to the user name ?Severn
L
67
SecurityContextHolder.getContext().getAuthentication().getPrincipal();

Returns the current user object. This can be User, UserDetails or your custom user object. You will need to cast the return object to UserDetails or your own user object if it is a custom one.

OR you can inject Authentication or Principal directly in to your controllers. Principle is your UserDetails/custom user object.

Note: UserDetails is an interface

Levirate answered 29/4, 2016 at 11:11 Comment(5)
Also a mkyong how-to w/ slightly more details.Representative
How to get the user's id?Landgrabber
There is a username (String) field in the User/UserDetails class/interface, if your user's id is the username.Levirate
I am getting java.lang.ClassCastException. please help meTeets
Can any of the objects in between be null?Paratuberculosis
P
29

you can use it like

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

it is in spring security reference http://docs.spring.io/spring-security/site/docs/4.0.2.RELEASE/reference/htmlsingle/#obtaining-information-about-the-current-user

Pyrography answered 17/8, 2015 at 14:23 Comment(2)
I am getting username but not user Object.Statuette
I think you have to check the next paragraph after the mentioned above docs.spring.io/spring-security/site/docs/4.0.2.RELEASE/… to get user details using UserDetailsServicePyrography
F
11

You can simply inject the Authentication Interface to your Controller and get the username of the logged in user, like below:

    @GetMapping(value = "/username")
    @ResponseBody
    public String currentUserName(Authentication authentication) {
    
        if (authentication != null)
            return authentication.getName();
        else
            return "";
    }
Find answered 9/11, 2018 at 6:40 Comment(0)
S
8

You just went one step foo far. SecurityContextHolder.getContext().getAuthentication() returns an Authentication object. You should know how you authenticated the user, and what can the the concrete class implementing Authentication. Assuming it is a subclass of AbstractAuthenticationToken (all Spring provided implementation are), and getDetails() returns a UserDetails, you can just use:

AbstractAuthenticationToken auth = (AbstractAuthenticationToken)
    SecurityContextHolder.getContext().getAuthentication();
UserDetails details = (UserDetails) auth.getDetails();
Severn answered 17/8, 2015 at 14:7 Comment(2)
I am getting an instance of WebAuthenticationDetails instead of UserDetails.Statuette
According to documentation getDetails is supposed to return additional information about authentication request (like IP addr), so it's not semantically correct to return UserDetails from this method.Accouter
R
8

I solved this problem by using SecurityContextHolder and Authentication.getName() :

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
        
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

String login = authentication.getName();

User user = usersService.getUserByLogin(login);
Remanent answered 23/3, 2021 at 17:39 Comment(0)
S
5

Since version 5.2 you can use CurrentSecurityContext annotation to get the current user authentication:

@GetMapping("/authentication")
public Object authentication(@CurrentSecurityContext(expression="authentication")
                             Authentication authentication) {
    return authentication.getDetails();
}

or even:

@GetMapping("/hello")
public String hello(@CurrentSecurityContext(expression="authentication.name")
                    String username) {
    return "Hello, " + username + "!";
}
Spasm answered 10/9, 2020 at 11:26 Comment(0)
F
3
public class User implements UserDetails {

    private String firstname;
    private String lastname;

}

assuming you have a custom user implementing UserDetails class

@RestController
@RequestMapping("/api/user")
class UsersController {

    @GetMapping
    public User fetchUser(@AuthenticationPrincipal User user) {
        return user;
    }
}
Fifi answered 20/5, 2023 at 17:36 Comment(0)
D
1

If you want to get all the attributes of your current user , first go to the class that implements UserDetails , more likely its called UserPrincipal and write a get method for each attribute like : getAge(), seconde go to you HTML file and write this

<span th:text="${#request.userPrincipal.principal.age}> </span>

And by the way you dont need to add any ModelAttribute in your controller Hope it fix the problem , and you can ask me

Dimer answered 1/12, 2020 at 19:58 Comment(4)
You didn't answer the initial question: "how to get the UserDetails object?"Camlet
@GetMapping("update") public String updateProfil(Authentication authentication ) { User u= userRepository.findByUsername(authentication.getName()).get(); System.out.println(u.getGender()); return "update"; }Dimer
@Controller public class HomeController { @Autowired UserRepository userRepository; private UserPrincipalDetailsService userPrincipalDetailsService; public HomeController(UserPrincipalDetailsService userPrincipalDetailsService) { this.userPrincipalDetailsService=userPrincipalDetailsService; } @GetMapping("") public String updateProfil(Authentication authentication ) { User u= userRepository.findByUsername(authentication.getName()).get(); //you can get anything from the object System.out.println(u.getGender()); return " "; }} Dimer
@KhalidTamine this should be either another answer, or an edit to an existing answer, but not a comment.Devlin
B
0

You can get current login user via:

  1. @Authenticationprincipal

  2. SecurityContextHolder.getContext().getAuthentication().getPrinciple()

Barth answered 27/9, 2020 at 20:9 Comment(1)
This is totally unclear. Do I need both of those things? Either/or? What do I do with those things? Where do I put them? Is it in a controller? A service? Please provide substantially more detail. As it stands this is not an answer.Devlin
C
0

you need to downcast principal to its implemented class, then you can extract context object which you set in securityContext.

 AbstractAuthenticationToken a = (AbstractAuthenticationToken) request.getUserPrincipal();
 UserContext context = (UserContext) a.getPrincipal();
Campos answered 5/3, 2021 at 21:16 Comment(0)
H
0

This solution worked for me with spring boot 2.5

First, define a User Principal class

public class UserPrincipal implements UserDetails {

    private static final long serialVersionUID = 1L;
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    // other methods ....
}

Second, define a User class:

public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String username;
    String password;
    //getters ans setters ...
}

Third, define a UserAuth class:

 public class UserAuth {
    
     public String getUsername()
     {
         UserPrincipal principal 
             = (UserPrincipal)SecurityContextHolder
                 .getContext()
                 .getAuthentication()
                 .getPrincipal();
         return principal.getUser().getUsername();
    }
}

Finally, you can auto-wire the UserAuth class as needed.

Heall answered 2/11, 2021 at 8:56 Comment(0)
V
-1

So almost every answer seems correct and feasible, kudos to all contributors, but to remove boilerplate code, could be useful and easy: make an Interface and its implementation that will contain all utility methods, and then simply @Autowire that.

public interface AuthHelper {
    Authentication getAuthentication();
    public String getName();
    public UserDetails getUserDetails()
}
@Component
public class AuthHelperImpl implements AuthHelper {

    @Override
    public Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
    public String getName() {
        return getAuthentication().getName();
    }
    public UserDetails getUserDetails() {
        return (UserDetails) getAuthentication().getPrincipal();
    }
//and more utilities you need
//you can also cast with UserPrincipal
}

now, at controllers:

@Controller
public class DemoController {
    @Autowired
    private AuthHelper authHelper;

    @RequestMapping(value = "/username", method = RequestMethod.GET)
    @ResponseBody
    public String currentUserNameSimple() {
        return authHelper.getName;
    }
}
Veg answered 10/1, 2022 at 14:27 Comment(0)
C
-2

This may be a good article to read. The article shows how to get the user information in a Spring application, starting with the common static access mechanism, followed by several better ways to inject the principal.

https://www.baeldung.com/get-user-in-spring-security

Carchemish answered 10/12, 2020 at 9:33 Comment(1)
This should be a comment, not an answer.Devlin

© 2022 - 2024 — McMap. All rights reserved.