Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security 5.7 - How to return custom UserDetails

I've seen a lot of examples where a user creates a custom UserDetailsService in order to override the loadUserByUsername method and return a custom implementation of a UserDetails object.

This was done previously with sth like this

@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

Now with the new version I'm confused on how to do this

I created a Bean and used the JdbcUserDetailsManager, I can configure my custom queries for users and authorities tables

  @Bean
  public UserDetailsManager userDetailsManager(DataSource dataSource) {
    String usersByUsernameQuery = "select username, password, enabled from tbl_users where username = ?";
    String authsByUserQuery = "select username, authority from tbl_authorities where username = ?";
    
    JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager(dataSource);
      
    userDetailsManager.setUsersByUsernameQuery(usersByUsernameQuery);
    userDetailsManager.setAuthoritiesByUsernameQuery(authsByUserQuery);
    
    return userDetailsManager;
  }

but how to return a custom UserDetails object with an extra field, e.g. an email with the new version?

like image 215
Apostolos Avatar asked Oct 25 '25 10:10

Apostolos


1 Answers

OK after many tries what I did was to remove completely JdbcUserDetailsManager stuff from my custom SecurityConfig class and I created a custom UserDetailsService and custom UserDetails class and it worked.

So security config class had no code regarding the authentication of the users. I was very confused because I thought that somehow I had to create a @Bean inside the config class, implement the authentication myself and in general that all this authentication code had to be done inside the config class, but it worked with this approach.

@Service
public class MyCustomUserDetailsService implements UserDetailsService {

  @Autowired
  UserRepository userRepository;
  
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    
    if (user == null) {
      throw new UsernameNotFoundException("User Not Found with username: " + username);
    }
    return MyUserDetails.build(user);
  }
}

And the details class

public class MyUserDetails implements UserDetails {

  private String username;
  private String firstName;
  private String lastName;

  @JsonIgnore
  private String password;

  private Collection<? extends GrantedAuthority> authorities;

  public MyUserDetails(String username, String firstName, String lastName, String password,
      Collection<? extends GrantedAuthority> authorities) {
    this.username = username;
    this.firstName = firstName;
    this.lastName = lastName;
    this.password = password;
    this.authorities = authorities;
  }

  public static MyUserDetails build(User user) {
    List<GrantedAuthority> authorities = user.getRoles().stream()
        .map(role -> new SimpleGrantedAuthority(role.getAuthority()))
        .collect(Collectors.toList());

    return new MyUserDetails(
        user.getUsername(), 
        user.getFirstName(),
        user.getLastName(),
        user.getPassword(), 
        authorities);
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return authorities;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  @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 o) {
    if (this == o)
      return true;
    if (o == null || getClass() != o.getClass())
      return false;
    MyUserDetails user = (MyUserDetails) o;
    
    return Objects.equals(username, user.username);
  }
}

Also check Spring Security Architecture

like image 153
Apostolos Avatar answered Oct 28 '25 03:10

Apostolos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!