Spring Security: Configuring UserDetailsService (Part 1)
Tags: springboot, springsecurity, java, api
Published: 2023-06-20 02:02:03


When we create an API for providing services or an web application, it is crucial to provide authentication and authorization to the project. In this post I am going to tell how to use Spring Security module to make the necessary security configurations.

To start, I am going to create a RestController with two endpoints "/", "/welcome" and try to run the application and see what happens.
```
@RestController
public class HomeController {	
   @GetMapping("/")
   public String sayHello() {
      return "Welcome to Spring Security!";
   }
   @GetMapping("/welcome")
   public String sayHello() {
      return "Welcome to Spring Security(Protected)!";
   }

}
```
Here is the result !

![base endpoint without spring security module](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1uc6hc44msrgca3yzwf.png)
Lets see what happens if we add spring security to the project dependency
```
<dependency>			 
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
```
Now if we run the application and try to hit and endpoint you will be redirected to a login page which will look like below.

![Endpoint after adding spring security](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rw72cva7g9zmuotg8r11.png)
Well, I don't remember adding a `/login` endpoint nor configured any users. But how the login page rendered and why?
Spring security is enabled just by adding the dependency to the classpath. By default all the routes/endpoints needs authentication to access. But what credentials are used to login? I don't remember configuring any credentials!. It is because if we didn't configure it spring will provide a default random credentials at run time

![auto generated password](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9kbs8ciuszdmwwn6iis6.png)

##Security Configuration

Spring MVC sends all incoming HTTP requests into a single servlet named `DispatcherServlet` after passing through set of filters. The DispatcherServlet is responsible to send the HTTP request to the corresponding Controller.
When we add Spring Security to the classpath a set of Security Filters are added to the FilterChain. These filters intercept every HTTP request before they come to the Controllers. This is where Spring security authenticates the HTTP requests and decides either to forward or reject the request.
![Spring Security Architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gm31etu2di2e5d8xq5bc.png)

By default, every HTTP request is authenticated. But, we can override this behaviour by making our own security configuration

In order to do that, we need to provide our custom `SecurityFilterChain` bean

Default behaviour is due to the bean
```
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
	http.authorizeHttpRequests().anyRequests().authenticated();
	http.formLogin();
	http.httpBasic();
	return http.build();
}
```

Now, we can have our own bean in a brand new configuration class like this.
```
@Configuration
class ApiSecurityConfiguration {	
   @Bean
   SecurityFilterChain customSecurityFilterChain(HttpSecurity http) throws Exception {
      http.authorizeHttpRequests((requests)-> requests
	       .requestMatchers("/welcome").authenticated()				 
               .anyRequests().authenticated());
	   .formLogin();
	   .httpBasic(Customizer.withDefaults());
      return http.build();
   }
}
```
The above configuration makes `/welcome` endpoint is authenticated and any endpoint other than that are publicly accessible

But we still have not set up our own set of users.
Before moving forward with Users setup, we need to understand the how the request is processed.

![UserDetailsService](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dyakllklvvh32s12y3n.png)

`UserDetailsService` (Interface) ` -> loadUserByUserName(String username)` 
This is the Core Interface which loads user specific data

`UserDetailsManager`(Interface extends UserDetailsService) Extension of userDetailsService which allows to create, update, delete UserDetails (see method signatures below)
- `createUser(UserDetails user)`
- `updateUser(UserDetails user)`
- `deleteUser(String username user)`
- `changePassword(String oldPwd, String newPwd)`
- `userExists(String username)`

 Sample Implementation classes ( for `UserDetailsManager` interface ) provided by Spring Security are `InMemoryUserDetailsManager`, `JdbcUserDetailsManager`, `LdapUserDetailsManager` 

> All the above interfaces and classes use an interface `UserDetails` which provides core user information

For proving user details for authetication, we need to implement `UserDetailsService` interface and provide it as a spring bean. We can either use the default implementations provided by spring or we can have our own custom implementation. 
If we are building enterprise application, it is recommended to have your own custom implementation. But for simplicity, we will use the default implementations provided 
In this post, we will see how we can configure `InMemoryUserDetailsManager`
```
@Bean
UserDetailsService userDetailsService() {
   UserDetails admin = User.withDefaultPasswordEncoder()						 
                           .username("admin")								 
                           .password("12345")								 
                           .authorities("admin")								 
                           .build();	
   UserDetails user = User.withDefaultPasswordEncoder()
			   .username("user")
			   .password("54321")
			   .authorities("read")
			   .build();
   return new InMemoryUserDetailsManager(user, admin);
	}
```
Here, we are using `DefaultPasswordEncoder()`. But, in general we need to provide a `PasswordEncoder` bean. Spring provides us with many password encoders
For Example, 
```
@Bean
PasswordEncoder passwordEncoder() {
   return new BCryptyPasswordEncoder()
}
```
Now, with these configurations, we can access the protected routes using the provided users in InMemory.
We can also get users from database using default `JdbcUserDetailsManager` bean offered by spring Security
In order to use this we need to have a datasource
We are going to use mysql for this application. Datasource configurations are done in `applications.properties` file or we have `application.yml`
```
spring:
  datasource:
    platform: mysql
    url: jdbc:mysql://localhost:5432/mydb
    username: foo
    password: bar
	jpa:
	   show-sql: true
	   properties:
	      hibernate:
		format_sql: true
```
By Default `JdbcUserDetailsManager` default implementation, assumes we follow a database  schema which has `users(id, username, password, enabled)` and `authorities(id, username, authority)` tables
So, if we are using `JdbcUserDetailsManager` implementation we need to follow the same ddl schema
Replace `InMemoryUserDetailsManager` bean with `JdbcUserDetailsManager` bean (Make sure you pass DataSoucrce object)
```
@Bean
UserDetailsService userDetailsService(DataSource dataSource) {
   return new JdbcUserDetailsManager(dataSource)
}
```
Well, we have configured our application to use InMemory UserDetails and configured to fetch user details from database as well using default implementations.

{% embed https://www.youtube.com/watch?v=LrRtwbgTe6g %}

But this is not enough for enterprise application, since we might not follow the same naming convention in the database based on client recommendation. In that case we cannot use `JdbcUserDetailsManager`. 

But that is a topic for another day.
I will write more about it in my next post.