Spring Security is a powerful and highly customizable authentication and access-control framework for Java applications. One of the simplest and most widely used authentication mechanisms supported by Spring Security is Basic Authentication. In this comprehensive guide, we’ll explore how to set up, configure, and customize Basic Authentication with Spring Security for securing your applications.

1. Overview

Basic Authentication is a simple authentication scheme defined in the HTTP specification. It involves sending the user’s credentials (username and password) in a Base64-encoded string as part of the request headers. While Basic Authentication is not the most secure authentication method, it can be useful in certain scenarios, such as securing internal APIs or providing a fallback authentication mechanism.

In this guide, we’ll cover the following topics:

  • Configuring Basic Authentication in Spring Security
  • Securing specific URLs or resources
  • Customizing the Basic Authentication entry point
  • Integrating Basic Authentication with an in-memory user store
  • Integrating Basic Authentication with a database-backed user store
  • Consuming a Basic Authentication-secured application

2. Configuring Basic Authentication in Spring Security

2.1 Java Configuration

To enable Basic Authentication in a Spring Security Java configuration, you can use the httpBasic() method within the HttpSecurity configuration. Here’s an example:

Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

In this configuration, we’re allowing all requests (anyRequest()) to be authenticated (authenticated()), and enabling Basic Authentication using the httpBasic() method.

2.2 XML Configuration

If you prefer to use XML configuration, you can enable Basic Authentication by including the <http-basic> element within the <http> configuration:

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

<authentication-manager>
    <!-- Configure authentication provider(s) -->
</authentication-manager>

Here, we’re intercepting all URLs (pattern="/**") and requiring authentication (access="isAuthenticated()"). The <http-basic> element enables Basic Authentication for the entire application.

3. Securing Specific URLs or Resources

In many cases, you might want to secure only specific URLs or resources with Basic Authentication while allowing anonymous access to other parts of your application. Spring Security provides flexibility in configuring authorization rules for different URL patterns.

3.1 Java Configuration

Java
@Bean
public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authz) -> authz
            .requestMatchers("/api/**").authenticated()
            .anyRequest().permitAll()
        )
        .httpBasic(Customizer.withDefaults());
    return http.build();
}

In this configuration, we’re allowing authenticated access only to URLs under the /api/** pattern using the requestMatchers() method. All other URLs are permitted for anonymous access using anyRequest().permitAll().

3.2 XML Configuration

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

Here, we’re using the <intercept-url> elements to define authorization rules. The first rule secures URLs under /api/** and requires authentication (access="isAuthenticated()"). The second rule allows anonymous access to all other URLs (access="permitAll").

4. Customizing the Basic Authentication Entry Point

By default, Spring Security’s BasicAuthenticationEntryPoint returns an HTML page for a 401 Unauthorized response. This behavior might not be desirable for certain scenarios, such as RESTful APIs, where you might prefer a custom response format (e.g., JSON).

4.1 Java Configuration

To customize the Basic Authentication entry point, you can create a custom implementation of the BasicAuthenticationEntryPoint and configure it in your HttpSecurity configuration:

Java
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.PrintWriter;

@Component
public class CustomBasicAuthEntryPoint extends BasicAuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authEx)
                                         throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName());

        PrintWriter writer = response.getWriter();
        writer.println("HTTP Status 401 - " + authEx.getMessage());
    }

    @Override
    public void afterPropertiesSet() {
        setRealmName("Your Realm Name");
        super.afterPropertiesSet();
    }
}

In this example, we’re creating a CustomBasicAuthEntryPoint class that extends BasicAuthenticationEntryPoint. We’re overriding the commence() method to customize the response format and the afterPropertiesSet() method to set the realm name.

Next, we need to configure the custom entry point in our HttpSecurity configuration:

Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private CustomBasicAuthEntryPoint authEntryPoint;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
            .authenticationEntryPoint(authEntryPoint);
        return http.build();
    }
}

Here, we’re injecting the CustomBasicAuthEntryPoint and configuring it using the authenticationEntryPoint() method within the httpBasic() configuration.

4.2 XML Configuration

To customize the Basic Authentication entry point in an XML configuration, you can define a custom bean and reference it in the <http-basic> element:

XML
<beans:bean id="customBasicAuthEntryPoint"
            class="com.example.CustomBasicAuthEntryPoint" />

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

Here, we’re defining a customBasicAuthEntryPoint bean and referencing it in the <http-basic> element using the entry-point-ref attribute.

5. Integrating Basic Authentication with a User Store

Spring Security provides several options for storing and retrieving user credentials. In this section, we’ll explore how to integrate Basic Authentication with an in-memory user store and a database-backed user store.

5.1 In-Memory User Store

For simplicity and testing purposes, you can configure an in-memory user store using the AuthenticationManagerBuilder in your Spring Security configuration.

Java Configuration

Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public UserDetailsManager userDetailsService() {
        UserDetails user1 = User.withUsername("user1")
                .password("{noop}user1Pass")
                .roles("USER")
                .build();
        UserDetails user2 = User.withUsername("admin")
                .password("{noop}adminPass")
                .roles("ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user1, user2);
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder = 
               http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(userDetailsService());
        return authenticationManagerBuilder.build();
    }
}

In this configuration, we’re defining two in-memory users (user1 and admin) with their respective passwords and roles using the inMemoryAuthentication() method within the AuthenticationManagerBuilder.

In the Spring Security configuration examples you provided, .password("{noop}adminPass") is used to specify that the password "adminPass" should be stored and used in plain text without any encoding or encryption.

The {noop} prefix is a special syntax used by Spring Security to indicate that the provided password string should not be encoded or encrypted. By default, Spring Security expects all passwords to be encoded using a specific password encoding algorithm (such as BCrypt or Pbkdf2).

XML Configuration

XML
<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user1" password="{noop}user1Pass" authorities="ROLE_USER" />
            <user name="admin" password="{noop}adminPass" authorities="ROLE_ADMIN" />
        </user-service>

Here’s the remainder of the comprehensive guide on Spring Security Basic Authentication:

5.2 Database-backed User Store

In a real-world application, you’ll likely store user credentials in a database or an external authentication system. Spring Security provides several options for integrating with database-backed user stores, such as JDBC or JPA.

JDBC User Store

To use a JDBC user store, you’ll need to configure a JdbcUserDetailsManager bean and provide the necessary database details. Here’s an example Java configuration:

Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
        manager.setUsersByUsernameQuery("SELECT username, password, enabled FROM users WHERE username = ?");
        manager.setAuthoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username = ?");
        return manager;
    }
}

In this configuration, we’re injecting a DataSource bean and creating a JdbcUserDetailsManager. We’re also providing the necessary SQL queries to retrieve user details and authorities from the database.

JPA User Store

If you’re using JPA in your application, you can configure a JPA-backed user store using the JpaUserDetailsService. Here’s an example Java configuration:

Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private UserRepository userRepository;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new JpaUserDetailsService(userRepository);
    }
}

In this configuration, we’re injecting a UserRepository (which extends JpaRepository) and creating a JpaUserDetailsService instance. Spring Security will automatically retrieve user details and authorities from the repository.

6. Consuming a Basic Authentication-secured Application

Once you’ve secured your application with Basic Authentication, you can consume it using various clients, such as web browsers, command-line tools like curl, or programmatic clients like RestTemplate.

6.1 Using curl

You can use the curl command-line tool to send requests to a Basic Authentication-secured application. Here’s an example:

curl -X GET -u user1:user1Pass http://localhost:8080/api/resource

In this command, we’re sending a GET request to the /api/resource endpoint, and providing the username (user1) and password (user1Pass) using the -u option.

6.2 Using a Web Browser

Most modern web browsers support Basic Authentication and will display a login prompt when accessing a secured resource. After providing valid credentials, the browser will store and automatically send the credentials with subsequent requests to the same origin.

6.3 Using RestTemplate

If you’re consuming the secured application programmatically from a Java client, you can use the RestTemplate class and provide the necessary credentials. Here’s an example:

Java
String credentials = "user1:user1Pass";
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());

HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(encodedCredentials);

HttpEntity<String> request = new HttpEntity<>(headers);

ResponseEntity<String> response = restTemplate.exchange(
    "http://localhost:8080/api/resource",
    HttpMethod.GET,
    request,
    String.class
);

In this example, we’re creating a HttpHeaders object and setting the Basic Authentication credentials using the setBasicAuth() method. We then create a HttpEntity with the headers and use the RestTemplate to send a GET request to the secured resource.

7. Conclusion

In this comprehensive guide, we’ve covered various aspects of configuring and customizing Basic Authentication with Spring Security. We’ve explored how to enable Basic Authentication using Java and XML configurations, secure specific URLs or resources, customize the Basic Authentication entry point, integrate with in-memory and database-backed user stores, and consume a Basic Authentication-secured application using different clients.

While Basic Authentication is a simple and widely supported authentication mechanism, it’s important to note that it transmits credentials in plain text (Base64-encoded) over the network. Therefore, it’s generally recommended to use Basic Authentication in combination with HTTPS to ensure the confidentiality of the credentials during transmission.

For more advanced authentication and authorization scenarios, Spring Security offers a wide range of features and integrations, such as form-based authentication, OAuth2, JWT, and more. However, Basic Authentication can still be a useful option in certain scenarios or as a fallback authentication mechanism.

By |Last Updated: May 19th, 2024|Categories: Spring Security|

Table of Contents