Issue
To display the currently logged in user's information, I need access to the user's ID as the username is not unique. The User details I get from getPrincipal() already contain an "id" field, yet I can't access it using this method (throws NoSuchFieldException).
@GetMapping("/account")
String account(Model model){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String userName= auth.getName();
Object Principal = auth.getPrincipal();
UUID id = Principal.getClass().getField("id");
The structure printed in the console looks like this: SpringSecurityAuthenticationManagement.UserAccountDetails(id=..., username=..., password=..., isEnabled=..., authorities=...). How can I access only the ID property?
Solution
Thanks for the question. There are several ways to display the currently logged-in user's information.
- From SecurityContextHolder
The SecurityContextHolder is where Spring Security stores the details of who is authenticated. Spring Security does not care how the SecurityContextHolder is populated. If it contains a value, it is used as the currently authenticated user.
@GetMapping("/account/your-custom-path-0")
public String getAccountIdFromSecurityContextHolder() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserAccountDetails userAccountDetails = (UserAccountDetails) authentication.getPrincipal();
UUID uuid = userAccountDetails.getId();
return uuid.toString();
}
Please refer to Spring Document.
- From HttpServletRequest
HttpServletRequest.getUserPrincipal() returns the result of SecurityContextHolder.getContext().getAuthentication(). This means that it is an Authentication, which is typically an instance of UsernamePasswordAuthenticationToken when using username- and password-based authentication. This can be useful if you need additional information about your user.
@GetMapping("/account/your-custom-path-1")
public String getAccountIdFromHttpServletRequest(HttpServletRequest httpServletRequest) {
UserAccountDetails userAccountDetails = (UserAccountDetails) httpServletRequest.getUserPrincipal();
UUID uuid = userAccountDetails.getId();
return uuid.toString();
}
Please refer to Spring Document.
- Using AuthenticationPrincipal Annotation
Spring Security provides AuthenticationPrincipalArgumentResolver, which can automatically resolve the current Authentication.getPrincipal() for Spring MVC arguments. By using @EnableWebSecurity, you automatically have this added to your Spring MVC configuration.
@GetMapping("/account/your-custom-path-2")
public String getAccountIdUsingAuthenticationPrincipalAnnotation(@AuthenticationPrincipal UserDetails userDetails) {
UserAccountDetails userAccountDetails = (UserAccountDetails) userDetails;
UUID uuid = userAccountDetails.getId();
return uuid.toString();
}
Please refer to Spring Document.
- From Authentication
The Authentication contains ... principal: Identifies the user. When authenticating with a username/password this is often an instance of UserDetails.
@GetMapping("/account/your-custom-path-3")
public String getAccountIdFromAuthentication(Authentication authentication) {
UserAccountDetails userAccountDetails = (UserAccountDetails) authentication.getPrincipal();
UUID uuid = userAccountDetails.getId();
return uuid.toString();
}
Please refer to Spring Document.
You may also refer to Baeldung - Retrieve User Information in Spring Security, which also gives you some information on this issue. Please tell me if the links above become invalid in the future.
Updated
The class UserAccountDetails needs to implement UserDetails like this:
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
public class UserAccountDetails implements UserDetails {
private UUID id;
private String username;
private String password;
public UserAccountDetails(UUID id) {
this.id = id;
}
// getters and setters
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null; // please use your own settings
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
UserAccountDetails that = (UserAccountDetails) object;
return Objects.equals(id, that.id)
&& Objects.equals(username, that.username)
&& Objects.equals(password, that.password);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password);
}
@Override
public String toString() {
// may be useful when debugging
return "UserAccountDetails {" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
Answered By - Xuan Chen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.