Spring Security with Custom UserDetailsService Implementation
UserDetailsService is specification around username, password and authorities.
This core interface which loads user-specific data from configured back end service. In this example shows how to
implement custom UserDetailsService
You can find the code here
This example creates a custom UserDetailsService backed by mongodb.
UserDetailsService declaration
@Component
public class CustomUserDetailsService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Unable to find user - " + username));
return withUsername(user.getUsername()).password(user.getPassword()).authorities(user.getAuthorities()).build();
}
}
PasswordEncoder interface for encoding password. NoOpPasswordEncoder was the default PasswordEncoder
prior to Spring Security 5.0 however from Spring Security 5.0 onwards default PasswordEncoder is
BCryptPasswordEncoder.
There are multiple ways you can declare the PasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Above declare use the default BCryptPasswordEncoder
Available mappings
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
User Model
In this example User model implements UserDetails interface.
@Document
public class User implements UserDetails {
private final String username;
private final String password;
private Set<GrantedAuthority> authorities;
private final String authority;
private final boolean active;
public User(String username, String password, boolean active, String authority) {
this.username = username;
this.password = password;
this.authority = authority;
this.active = active;
this.authorities = Set.of(new SimpleGrantedAuthority(authority));
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return true;
}
}
Enable authentication for every request by extending WebSecurityConfigurerAdapter
By extending WebSecurityConfigurerAdapter you can do the following
- Set authentication restriction around the request
- Create User
- Can enable http basic and form based authentication.
public class CustomerUserDetailsSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
http.authorizeRequests().anyRequest().authenticated();
}
}