How to Implement Basic Authentication in Spring Cloud Gateway using Spring Security

Whether you expose your API through an API gateway or directly from the service, API security is important to protect your API from potential security threats and unauthorised access. The API consumer should validate themselves before the API access is granted. Strong authentication mechanism can ensure that only authorised users or applications can access the API. This might include Basic Authentication, API Keys, Bearer Token, OAuth, JWT Token, etc. In this article, we will cover how to secure your API with Basic Authentication in Spring Cloud Gateway using spring security.

Implementing basic authentication in Spring Cloud Gateway enables you to centralize authentication logic and enforce security policies across multiple services. Safeguarding an API requires going through several steps. Below, I’ll outline the detailed changes and configurations required to achieve this:

1. Create a Spring Cloud Gateway Project:

If you already don’t have Spring Cloud Gateway project, create it using Spring Boot. You can use Spring Initializer, Spring Boot CLI or your preferred IDE.

2. Add Dependencies:

Ensure you have the necessary Spring Security dependencies in your pom.xml or build.gradle file.

<dependencies>
    <!-- Spring Cloud Gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

Changes in your build.gradle file as shown below.

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    implementation 'org.springframework.boot:spring-boot-starter-security'
}

By default, basic authentication will be enabled once spring security dependency is added.

3. Configuration:

Add routing & other configuration changes in your application.yaml file.

spring:
  cloud:
    gateway:
      routes:
        - id: post_service
          uri: http://localhost:8081   # Replace with your target URI
          predicates:
            - Path=/post/*            # Replace with your endpoints

Make sure to replace http://localhost:8081 with the actual URI of your backend service and configure other settings as needed for your specific use case.

Note: For this example, we have created a post service and we are routing to this service through the gateway. Its source code, alongside the tutorial’s source, can be accessed here.

4. Security Configuration:

Lets create a security config class to configure Spring Security. we have to annotated this class with @Configuration annotation. Inside this class, create a method which will be annotated with @Bean annotation & this method will return object of type SecurityWebFilterChain. So ultimately we are creating a bean of type SecurityWebFilterChain. Since Spring cloud gateway works on reactive programming model, we need to create a bean of type SecurityWebFilterChain instead of SecurityFilterChain.

This method accept parameter of type ServerHttpSecurity. It is used to configure security rules.

SecurityWebFilterChain:

  • SecurityWebFilterChain is a key component in Spring Security for configuring security filters that are applied to WebFlux applications.
  • It allows you to define a chain of security filters that handle authentication, authorization, and other security concerns for HTTP requests in a reactive (non-blocking) environment.

ServerHttpSecurity:

  • ServerHttpSecurity is a builder object provided by Spring Security for configuring security rules for WebFlux applications.
  • It allows you to specify various security configurations such as authentication mechanisms, authorization rules, CSRF protection, session management, and more.
  • You can use ServerHttpSecurity within the SecurityWebFilterChain configuration method to define the security rules for your application.

Refer below code snippet,

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {

        return http.authorizeExchange(exchange -> exchange.pathMatchers("/post/list")
              .permitAll()
              .anyExchange()
              .authenticated()
          ).httpBasic(Customizer.withDefaults()).build();

    }

}

In above code, we have created a class with name SecurityConfig & annotated with @Configuration annotation. Inside this class, we have created a bean of SecurityWebFilterChain. Inside filterChain method, we specify authorization rules using authorizeExchange(), allowing unrestricted access to “/post/list” endpoint and enabled basic authentication for rest of the endpoints.

5. Create a Custom ReactiveAuthenticationManager:

Since spring cloud gateway is reactive application, we can use ReactiveAuthenticationManager interface. This interface is responsible for authenticating a user based on the provided credentials. It is used in reactive(non-blocking) applications, for non-reactive(blocking) application AuthenticationManager interface is used. It is a key component in the authentication process.

ReactiveAuthenticationManager interface contains a single method authenticate, which takes an Authentication object representing the user’s credentials and returns a Mono<Authentication> representing the authenticated user.

You can implement ReactiveAuthenticationManager to define your own authentication logic. For example, you might authenticate users against a database, LDAP server, OAuth provider, or any other authentication source. In order to use this class object by spring security, we need to annotated this class with @Component annotation.

Here’s a simplified example of implementing a custom ReactiveAuthenticationManager:

@Component
public class GatewayAuthManager implements ReactiveAuthenticationManager {

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        final String name = authentication.getName();
        final String password = authentication.getCredentials().toString();
        if (("test-user".equals(name) && "test-pwd".equals(password))) {
            return Mono.just(new UsernamePasswordAuthenticationToken(name, password, Collections.emptyList()));
        }
        return null;
    }
}

In the above code, we have created a class GatewayAuthManager. It implements ReactiveAuthenticationManager interface. This class is annotated with @Component annotation. We have implemented authenticate method & inside this method we have written the authentication logic. Here it checks the provided username and password against hardcoded values just for demonstration purposes but you can change this authentication logic to validate with other authentication source like LDAP, static files, database, etc. On successful authentication, we can return object of UsernamePasswordAuthenticationToken.

5. Run Your Gateway:

Start your Spring Cloud Gateway application. It will now enforce basic authentication for the endpoints.

Note: Also start the services that you want to route through the gateway.

6. Testing

You can perform testing in various ways once the Spring cloud API gateway is started.

You can test it through,

  • Postman
  • Curl

Testing through Postman:

Open the Postman app & put the REST endpoint in the address bar as shown below.

In above image, we have put REST endpoint URL & click on send button, status code 401 unauthorised is returned from the api gateway. Here we have not specified any authentication method in Authorization tab of Postman , hence we got this status code.

Now lets specify the basic authentication along with username & password in Authorization tab.

Here, we have set Basic Authentication method along with username & password and clicked send button. The service returned 200 OK status code with the response in json format.

Note: We have highlighted the necessary points in the above images.

Testing through Curl:

You can test the endpoint using curl as shown below

curl -u user:password http://localhost:8080/your-api-endpoint

You can also test some endpoints(i.e: endpoints with HTTP GET method) through browser. Since we have added the basic authentication, a browser shows login screen to enter the credentials. If the credentials are invalid, it returns a response with a status code of 401 Unauthorized otherwise browser displays the endpoint response.

The entire source code for this article can be found here

Conclusion:

In conclusion, implementing Basic Authentication in Spring Cloud Gateway using Spring Security provides a robust solution for securing access to your microservices or APIs. By leveraging the power of Spring Security within the gateway, you can ensure that only authenticated users are granted access to your endpoints, thus enhancing the security of your system.

Through the steps outlined in this article, you have learned how to configure Spring Security to enforce Basic Authentication for incoming requests to your gateway routes. By defining custom security configurations, you can tailor the authentication process to suit your specific requirements.

Furthermore, integrating Basic Authentication with Spring Cloud Gateway enables you to centralize authentication logic and enforce security policies across multiple services, promoting consistency and maintainability in your architecture. This approach also allows you to easily scale your system and adapt to evolving security needs as your application grows.

With the knowledge gained from this article, you are well-equipped to enhance the security of your Spring Cloud Gateway application by implementing Basic Authentication using Spring Security. By prioritizing security in your architecture, you can build trust with your users and protect your valuable resources from unauthorized access and malicious attacks.

2 Comments on “How to Implement Basic Authentication in Spring Cloud Gateway using Spring Security

  1. when I provide wrong user/password, it doesn’t return 401 nor 403 but 500 “The mapper returned a null Publisher”

Leave a Reply

Your email address will not be published. Required fields are marked *

*