DEV Community

Vincent Caunegre
Vincent Caunegre

Posted on

Basic authentication in Spring Boot

Spring Security is responsible for authenticate and authorize Spring applications, it provide multiple way of doing authentication but Basic is probably the simplest.

With basic authentication, for every request made to the api, we send the user credentials in the headers, generally encoded with Base64 format.

Creating the Spring Boot application

We keep it simple with only spring web and spring security.

Image description

We will add a simple GET request:

package com.example.spring_basic;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/hello")
    public String hello(){
       return "hello world !";
    }
}
Enter fullscreen mode Exit fullscreen mode

If we run the application and we inspect the logs, we can see that Spring is already handling security with a generated password

Image description

We can already test the request, for instance with IntelliJ Http request, and see that it works:

### GET request to example server
GET http://localhost:8080/hello
Authorization: Basic user <Generated password>
Enter fullscreen mode Exit fullscreen mode

However this is not what we want, so let's add a new Spring Security Configuration:

package com.example.spring_basic;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.httpBasic(withDefaults());
        httpSecurity.authorizeHttpRequests(http ->{
            http.requestMatchers("/users").permitAll();
            http.anyRequest().authenticated();
        });
        return httpSecurity.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
        UserDetails user= User.builder().username("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER").build();

        UserDetails admin=User.builder().username("admin")
                .password(passwordEncoder().encode("password"))
                .roles("USER","ADMIN").build();
        return new InMemoryUserDetailsManager(user, admin);
    }
}

Enter fullscreen mode Exit fullscreen mode

First we recreate a SecurityFilterChain bean, for now we only add basic auth and require all requests to be authenticated.

Then we declare a PasswordEncoder bean that will be used to encode the password during the security process.

Then we declare a UserDetails implementation. For the moment we will not create custom UserDetails and User. You can see that we use the passwordEncoder to encode the "password" value. So when we will compare the provided password with the user password we will compare the encoded version of the password.

We can test the request by providing credentials:

### GET request to example server
GET http://localhost:8080/hello
Authorization: Basic user password
Enter fullscreen mode Exit fullscreen mode

It should works, but how ?

Behind the scene

Image description

When we make the request, it flows through a filter chain with differents filters, when a filter detects the headers he wants, he can call the AuthenticationManager. In our case, when the BasicAuthenticationFilter receive a request with username and password, it call the AuthenticationManager with a non valided UsernamePasswordToken.

The AuthenticationManager will then find a provider that handle this token, in our case the DaoAuthenticationProvider. It will use the UserDetails to find User with the same username as the credentials, then encode the password and compare it with the credentials password. If it matchs, it will update the token, add it to the SecurityContext, and return the token to the manager, then to the filter.


You can find the source code here

Top comments (0)