Basic Auth with Spring Security

Learn to configure basic authentication in an application secured with Spring security.

1. What is Basic Auth?

Basic authentication is often used with stateless clients who pass their credentials on each request. It’s quite common to use it in combination with form-based authentication where an application is used through both a browser-based user interface and as a webservice. However, basic authentication transmits the password as plain text so it should only really be used over an encrypted transport layer such as HTTPS.

Because a basic authentication header has to be sent with each HTTP request, the web browser needs to cache the credentials for a reasonable period to avoid constant prompting the user for the username and password. Caching policy differs between browsers.

The client sends HTTP requests with the Authorization header that contains the word Basic word followed by a space and a base64-encoded string username:password.

For example, to authorize as user / password the client would send:

Authorization: Basic dXNlcjpwYXNzd29yZA==

2. Default Basic Auth Configuration

The security-related packages and classes are part of the spring security module so let us start with importing the module, first.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

The simplest possible solution to implement basic HTTP authentication is to use “http-basic” tag in spring security configuration file like this.

<http>
  <intercept-url pattern="/**" access="isAuthenticated()" />
  <http-basic />
</http>

The equivalent Java configuration is:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) 
	throws Exception {

	http.authorizeRequests()
	    ...
	    .httpBasic()
	    ...
	return http.build();
}

The above configuration will force the user to authenticate before accessing any webpage or any other resource in our application.

Interesting thing is that we do not need to create any login page or session management mechanism. The browser will present a login box before the user on our behalf. And because each request contains authentication information just like in HTTP stateless mechanism, we do not need to maintain the session also.

By default, spring will create a user with username “user” and generated password is printed in the console. It can be used to authenticate into the application.

Using generated security password: 38b2815c-9943-4b94-b5b6-7adcd5700d10

3. Custom Basic Auth Configuration

In the following configuration, we have customized a few things:

  • We are using InMemoryUserDetailsManager to configure a user (user:password) in place of the default user. We can create as many users as we want, with required authorities assigned to each user.
  • Configured the default password encoder to BCryptPasswordEncoder.
  • Secured all URLs, except '/public‘ that is allowed to all.
<http auto-config="true"  use-expressions="true">
	<intercept-url pattern="/public" access="permitAll" />
	<intercept-url pattern="/**" access="isAuthenticated()" />
	<http-basic entry-point-ref="authenticationEntryPoint" />
</http>

<authentication-manager alias="authenticationManager">
	<authentication-provider>
	  <user-service>
	    <user name="user" 
                  password="$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG" 
                  authorities="ROLE_USER" />
	  </user-service>
	  <password-encoder ref="bCryptPasswordEncoder" />
	</authentication-provider>
</authentication-manager>

<bean id="authenticationEntryPoint"
  class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
     <property name="realmName" value="howtodoinjava" />
</bean>

<bean id="bCryptPasswordEncoder"
    class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
    <constructor-arg value="8"/>
</bean>

The equivalent Java configuration is:

@Configuration
public class BasicAuthWebSecurityConfiguration
{
  @Autowired
  private AppBasicAuthenticationEntryPoint authenticationEntryPoint;

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/public").permitAll()
        .anyRequest().authenticated()
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint);
    return http.build();
  }

  @Bean
  public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User
        .withUsername("user")
        .password("$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG")
        .roles("USER_ROLE")
        .build();
    return new InMemoryUserDetailsManager(user);
  }

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

4. Custom BasicAuthenticationEntryPoint

The authentication entry points are used by the ExceptionTranslationFilter to commence authentication. By default, the BasicAuthenticationEntryPoint returns a full page for a 401 Unauthorized response back to the client.

To customize the default authentication error page used by basic auth, we can extend the BasicAuthenticationEntryPoint class. Here we can set the realm name and well as the error message sent back to the client.

@Component
public class AppBasicAuthenticationEntryPoint
  extends BasicAuthenticationEntryPoint {

  @Override
  public void commence(HttpServletRequest request,
                       HttpServletResponse response,
                       AuthenticationException authEx) throws IOException {

    response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    PrintWriter writer = response.getWriter();
    writer.println("HTTP Status 401 - " + authEx.getMessage());
  }

  @Override
  public void afterPropertiesSet() {
    setRealmName("howtodoinjava");
    super.afterPropertiesSet();
  }
}

5. Consuming Basic Auth

5.1. In Browser

Start the server and load any non-protected URL in the browser. A login window appears. Please note that it is browser generated login box and the application has only provided relevant headers to the browser.

http-basic-authenication-9270993
HTTP basic authentication window

Enter the incorrect username and password. This will make the browser again present the cleared login box or it will show the error page in some cases like this.

http-basic-authetication-error-9805120
Login error in HTTP basic authentication

When we enter the correct username and password, the correct response is loaded in the browser

5.2. Using JUnit

To authenticate using basic auth, for accessing a resource is unit tests, we can use the MockMvcRequestBuilders.with() method when using MockMvc.

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.MOCK,
    classes = {
      BasicAuthWebSecurityConfiguration.class,
      AppBasicAuthenticationEntryPoint.class,
      AppController.class
    }
)
@AutoConfigureMockMvc
class BasicAuthTest {

    @Autowired
    private MockMvc mvc;

    @Test
    void expectOKResponse_WhenAccessNotSecuredURL() throws Exception {
        ResultActions result = mvc.perform(MockMvcRequestBuilders.get("/public"))
          .andExpect(status().isOk());
    }

    @Test
    void expectUnauthorizedUser_WhenPasswordIsWrong() throws Exception {
        ResultActions result = mvc.perform(MockMvcRequestBuilders.get("/")
                .with(httpBasic("user","wrong-password")))
            .andExpect(status().isUnauthorized());
    }

    @Test
    void expectOKResponse_WhenPasswordIsCorrect() throws Exception {
        ResultActions result = mvc.perform(MockMvcRequestBuilders.get("/")
            .with(httpBasic("user", "password")))
          .andExpect(content().string("Hello World !!"));
    }
}

6. Conclusion

In this tutorial, we learned about the default basic authentication commissioned by the Spring security module. We also learned to customize and configure various components involved in the basic authentication including password encoding and custom username and passwords.

Happy Learning !!

Sourcecode on Github

Leave a Reply

0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.