How to Get Logged-in User's Details with Spring Security
- Details
- Written by Nam Ha Minh
- Last Updated on 24 August 2020   |   Print Email
package net.codejava; import java.util.*; import javax.persistence.*; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String email; private String password; @Column(name = "full_name") private String fullName; private boolean enabled; @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) @JoinTable( name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>(); // constructors, getters and setters are not shown for brevity }And the entity class Role as below:
package net.codejava; import javax.persistence.*; @Entity @Table(name = "roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private String description; // constructors, getters and setters are not shown for brevity }To use Spring Security for authentication and authorization, we code a class that implements the UserDetails interface as follows:
package net.codejava import java.util.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class ShopmeUserDetails implements UserDetails { private User user; public ShopmeUserDetails(User user) { this.user = user; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { Set<Role> roles = user.getRoles(); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getEmail(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return user.isEnabled(); } }You can see this UserDetails class wraps the User entity class.And code a class that implements the UserDetailsService, which overrides the loadUserByUsername() method for authentication as below:
package net.codejava; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.*; public class ShopmeUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { User user = userRepository.getUserByEmail(email); if (user == null) { throw new UsernameNotFoundException("Could not find user with that email"); } return new ShopmeUserDetails(user); } }As you can see, when a user has logged in successfully, a ShopmeUserDetails object is returned to Spring Security, and it will be set as the principal object in the security context.So when we retrieve the object that represents the currently logged-in user in the application, we actually get the object returned by the loadUserByUsername() method, which wraps an object of the User entity class.
1. Display the default username
<span>[[${#request.remoteUser}]]</span>Or using Thymeleaf Extras for Spring Security:
<span sec:authentication="name">Username</span>However, this way always prints the value returned from the getUsername() method in the UserDetails class (ShopmeUserDetails in this article).What if we want to display another attribute of the user, e.g. first name, last name or fullname?We can change the value returned by the getUsername() method, but it will break other functions that depend on the username, e.g. Remember me feature provided by Spring Security.Read the next section to know how.
2. Display any user’s information (first name, last name, fullname…)
Suppose that we want to display the full name of the currently logged-in user instead of email. Add a getter method in the UserDetails class as shown below:public class ShopmeUserDetails implements UserDetails { private User user; // override methods from UserDetails... public ShopmeUserDetails(User user) { this.user = user; } public String getFullName() { return this.user.getFullName(); } }Then in the view, you can display full name of the user as follows:
<span sec:authentication="principal.fullName">Fullname</span>Here, the principal object is actually a UserDetails object returned by the loadUserByUsername() method - So we can access any properties in this object. For example, if we have first name:
<span sec:authentication="principal.firstname">Firstname</span>Note that to use sec:authentication attribute, you must declare this dependency:
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> </dependency>And declare an XML namespace:
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
3. Display user’s assigned roles
To show the assigned roles (authorities) of the currently logged-in user, write the following line of code:<span sec:authentication="principal.authorities">Roles</span>It will call the getAuthorities() method of the UserDetails class.
4. Get UserDetails object in Spring Controller
Certainly there will be a case in which we want to get the UserDetails object that represent the currently logged-in user in Java code, e.g. a handler method a Spring controller class. The simplest way is using the @AuthenticationPrincipal annotation as shown in the example below:package net.codejava; import org.springframework.security.core.annotation.AuthenticationPrincipal; @Controller public class AccountController { @Autowired private UserServices service; @GetMapping("/account") public String viewUserAccountForm( @AuthenticationPrincipal ShopmeUserDetails userDetails, Model model) { String userEmail = userDetails.getUsername(); User user = service.getByEmail(userEmail); model.addAttribute("user", user); model.addAttribute("pageTitle", "Account Details"); return "users/account_form"; } }As you can see, we can declare the type of the UserDetails object right after the @AuthenticationPrincipal annotation – very simple and convenient, right?If you want to see the coding in action, I recommend you to watch the video below:
My Spring Security Tutorials:
- Spring Boot Security Form Authentication with in-memory users
- Spring Boot Security HTTP Basic Authentication with in-memory users
- Spring Boot Security Form Authentication with JDBC and MySQL
- Spring Boot Security Authentication with JPA, Hibernate and MySQL
- Spring Boot Security Role-based Authorization Tutorial
- Spring Boot Security Customize Login and Logout
- Spring Security: How to use Logout Link instead of Button
Other Spring Boot Tutorials:
- How to create a Spring Boot Web Application (Spring MVC with JSP/ThymeLeaf)
- Spring Boot CRUD Example with Spring MVC – Spring Data JPA – ThymeLeaf - Hibernate - MySQL
- Spring Boot Hello World RESTful Web Services Tutorial
- Spring Boot Thymeleaf Form Handling Tutorial
- Spring Data JPA Paging and Sorting Examples
- Spring Boot Error Handling Guide
- Spring Boot Logging Basics
Comments