In the Spring Boot authentication process, I investigated how to specify a password that I had been wondering for a long time.
We will show you how to implement authentication process with user name and password in Spring Boot and Spring Security.
The prerequisite environment is as follows.
| Version | |
|---|---|
| Java | 1.7 | 
| Spring Boot | 1.5.9.RELEASE | 
If you go to "Spring Boot Authentication Process", you will find many samples using  loadUserByUsername of `ʻUserDetailsService`` [^ fqcn-uds].
I have also written. → Use Basic authentication with Spring Boot
However, as shown below, the argument of  loadUserByUsername is only `ʻusername``.
UserDetailsService
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
Therefore, it cannot be handled when ** authentication process with user name and password ** is required.
For example, in the following cases,  loadUserByUsername cannot be used.
--Password hashing is performed on the DB side [^ cript], and it is necessary to include the password in the SQL parameter value. --Authentication is performed in an external library, and it is necessary to pass a user name and password set to the API.
[^ cript]: PostgreSQL  crypt function, etc.
Use ʻAbstractUserDetailsAuthenticationProvider`` [^ fqcn-audap] instead of ʻUserDetailsService.  Specifically, by creating a Bean of `ʻAuthenticationProvider using` ʻAbstractUserDetailsAuthenticationProvider``, ** authentication process using user name and password ** can be easily implemented.
Register Authentication Provider with Java Config
@Configuration
public class MyConfigure extends WebSecurityConfigurerAdapter {
  //* Basic authentication is required for all URLs to check the operation.
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().realmName("My sample realm"); //Basic authentication settings
    http.authorizeRequests().anyRequest().authenticated(); //Setting requests that require authentication
  }
  // ----------------------------------------
  //Bean definition
  // ----------------------------------------
  @Bean
  public AuthenticationProvider getAuthenticationProvider() {
    //Use your own AuthenticationProvider
    return new MyUserDetailsAuthenticationProvider();
  }
}
Definition of your own Authentication Provider
//By inheriting AbstractUserDetailsAuthenticationProvider, it is OK to implement only retrieveUser
public class MyUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
  private static final String DUMMY_PASSWORD = "DUMMY_PASSWORD"; //* Since it is not used for authentication, any value can be used.(Null and empty string are NG)
  private static final List<GrantedAuthority> AUTH_USER = AuthorityUtils.createAuthorityList("USER"); //* In this sample, all have this authority.
  // <<< N/A >>>
  @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {}
  //---
  @Override
  protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    String userId = username;
    String password = (String) authentication.getCredentials();
    //Check user ID and password
    boolean isValid = AuthApi.isValidUserIdAndPassword(userId, password); //* Pseudo code that authenticates with the API of the external library
    if (!isValid) { throw new UsernameNotFoundException(username); }
    //Implementation of UserDetails(User)Is generated and used as the return value
    return new User(username, DUMMY_PASSWORD, AUTH_USER);
  }
}
As I wrote at the beginning, there are a lot of samples using  loadUserByUsername, but for some reason there seemed to be few samples using passwords for authentication processing, so I looked it up and wrote it.
If you have any mistakes or improvements, please let us know.
Recommended Posts