Hello! Welcome back to our journey into building production-ready microservices.
In our last lesson, we successfully secured an individual microservice by configuring it as an OAuth 2.0 Resource Server. This is a common and robust pattern, but it requires each service to independently handle token validation. Today, we'll explore a different architectural approach that is central to many large-scale systems.
Our learning outcome is to implement JWT token validation at the API Gateway for centralized authentication. By handling security at the edge of our system, we can simplify our downstream services and enforce security policies consistently. This is a critical pattern and a frequent topic in senior-level system design interviews.
Why Centralize Authentication at the Gateway?
Before we dive into the implementation, let's discuss the "why". In the previous model, every service was its own bouncer, checking IDs at the door. In the centralized model, the API Gateway acts as a single, highly-secure entrance for the entire complex.

The key benefits of this approach are:
- Simplified Microservices: Your business services (e.g.,
product-service,order-service) no longer need to contain security validation logic. They can focus purely on their business domain. - Consistent Security Policy: All security rules are enforced in one place, reducing the risk of inconsistent or misconfigured security in one of the many downstream services.
- Reduced Network Latency: Downstream services don't need to make extra calls to an authorization server's
/userinfoor/introspectendpoints. The JWT is validated once at the edge. - Single Point of Attack: While this sounds like a negative, it allows you to focus your most robust security measures (like rate limiting, WAF, advanced monitoring) on a single component: the gateway.
The Mechanism: Spring Cloud Gateway Filters
Spring Cloud Gateway is built on a foundation of filters. A GatewayFilter allows you to intercept and modify requests before they are routed to a downstream service, and to modify the response on its way back to the client.
For our use case, we will create a custom authentication filter with the following logic:
- Intercept every incoming request.
- Check if the request is for a public endpoint (e.g.,
/auth/token). If so, let it pass through. - For protected endpoints, extract the
Authorizationheader. - Validate the JWT found in the header.
- If the token is valid, allow the request to be routed to the target service.
- If the token is missing or invalid, reject the request immediately with a
401 Unauthorizederror.
Let's see how to implement this in code.
Implementing a Custom Authentication Filter
One of the most direct ways to implement gateway authentication is by creating your own custom filter. This gives you complete control over the validation logic and is an excellent way to understand the underlying mechanics.
The following video provides a detailed, step-by-step walkthrough of building this exact architecture. It involves creating a dedicated identity-service (like our Authorization Server from the previous lesson) and then implementing a custom filter in the API Gateway.
Microservices Security Using JWT | Spring Cloud Gateway | JavaTechie
The video "Microservices Security Using JWT" by Java Techie demonstrates the entire end-to-end flow. We will focus on the API Gateway implementation, which shows how to build and apply a custom authentication filter.
Please watch the following segments: Architecture Overview (1:31 - 6:01): This sets the stage and explains the high-level design, reinforcing the 'why' behind centralizing security at the gateway. Filter Implementation (55:50 - 1:16:44): This is the core of the lesson. Pay close attention to how the AuthenticationFilter class is created. Note how it checks for the Authorization header, extracts the token, and uses a JwtUtil class to perform the actual validation. Also, observe how this filter is applied to specific routes in the application.yml file. Live Demonstration (1:16:44 - 1:25:04): Watch how a request without a token is rejected by the gateway, and a request with a valid token is successfully routed to the downstream service. This visualizes the filter in action.
Key Takeaways from the Implementation
Let's summarize the crucial parts of the implementation shown in the video:
-
AuthenticationFilterClass:- The filter extends
AbstractGatewayFilterFactory. This is a common base class for creating custom filters in Spring Cloud Gateway. - The core logic resides in the
applymethod. - It uses a
RouteValidatorto define a whitelist of public endpoints (like/auth/registerand/auth/token) that should bypass authentication.
- The filter extends
-
JWT Validation Logic:
- The filter extracts the token from the
Authorization: Bearer <token>header. - It reuses the exact same JWT parsing and validation logic (the
JwtUtilclass) from theidentity-service. This is a critical point: the service that issues the token and the service that validates it must share the same algorithm, secret key, or public key. - If validation fails (e.g., signature is invalid, token is expired), an exception is thrown, which results in an error response to the client.
- The filter extracts the token from the
-
Applying the Filter in
application.yml:- The custom filter is enabled on a per-route basis. This gives you fine-grained control over which services are protected.
spring: cloud: gateway: routes: - id: swiggy-app uri: lb://SWIGGY-APP predicates: - Path=/swiggy/** filters: - name: AuthenticationFilter # Our custom filter - id: identity-service uri: lb://IDENTITY-SERVICE predicates: - Path=/auth/**Notice the
identity-serviceroute does not have the authentication filter. This is essential, as clients need to be able to access it to get a token in the first place!
Test your understanding!
In the video's implementation, the JWT validation logic (the JwtUtil class) was copied from the identity-service into the api-gateway project. What is a major drawback of this "copy-paste" approach, and how could you improve it in a real-world project to avoid this issue?
Show answer
The major drawback is code duplication. If you ever need to change the signing secret, the token expiration logic, or the signing algorithm, you must remember to update it in both the identity-service and the api-gateway. Forgetting to do so would break authentication for the entire system.
A better approach would be to package the JwtUtil class and any related DTOs into a shared library (a separate Maven/Gradle module). Both the identity-service and api-gateway projects could then include this library as a dependency, ensuring they always use the exact same, single-source-of-truth implementation for JWT handling.
An Alternative: The Spring Security Integrated Approach
Writing a custom filter provides great insight, but for standard scenarios, Spring Security offers a more declarative and integrated way to achieve the same goal.
Remember how in the last lesson we made our microservice a Resource Server? We can apply the exact same concept to the API Gateway. By including the spring-boot-starter-oauth2-resource-server dependency in our gateway and configuring our SecurityWebFilterChain (the reactive equivalent of SecurityFilterChain), we can leverage Spring's built-in JWT validation capabilities.
JWT Auth in Spring Cloud Gateway: A Step-by-Step Guide
The article "JWT Auth in Spring Cloud Gateway" provides an excellent, concise example of this integrated approach. It contrasts the custom filter method with a more declarative one using Spring Security's native features.
Please read the introduction and then focus on the sections detailing the dependencies and Approach 1: Custom WebFilter. Notice how the JwtWebFilter and the SecurityConfig work together. This is a slightly different but conceptually similar take on the custom filter from the video.
The approach shown in the article uses a standard WebFilter and EnableWebFluxSecurity. A more streamlined version, when using a standard OIDC-compliant Authorization Server, would configure the gateway as a resource server directly:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchanges -> exchanges
.pathMatchers("/auth/**").permitAll() // Public endpoint
.anyExchange().authenticated() // All others require authentication
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt()); // Enable JWT validation
return http.build();
}
}
And in your application.yml, you'd simply point to the issuer URI, just as we did in the last lesson:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:9000 # Your Auth Server
Architectural Choice: Custom Filter vs. Integrated Security
This presents you with a key architectural decision, a perfect topic for an interview discussion.
-
Custom Filter Approach (like the video):
- Pros: Maximum flexibility to handle non-standard tokens, custom validation rules, or complex logic that isn't just about signature/claim validation.
- Cons: More boilerplate code to write and maintain. You are responsible for the security implementation.
-
Integrated Resource Server Approach:
- Pros: Far less code. Highly declarative. Leverages a battle-tested Spring Security implementation. Automatically handles fetching public keys from a
jwks_uri. - Cons: Less flexible if you have very specific, non-standard requirements. It assumes you are working with standard JWTs and an OAuth2/OIDC provider.
- Pros: Far less code. Highly declarative. Leverages a battle-tested Spring Security implementation. Automatically handles fetching public keys from a
For most production systems using standard OAuth2, the Integrated Resource Server approach is preferred due to its simplicity and robustness.
Conclusion
You have now learned how to fortify the entry point to your microservices ecosystem. Centralizing authentication at the API Gateway is a powerful pattern that simplifies your overall architecture and strengthens your security posture.
Key Takeaways:
- The API Gateway is the ideal place to handle cross-cutting concerns like authentication.
- Spring Cloud Gateway's
GatewayFiltermechanism allows you to intercept and validate requests before they reach downstream services. - You can implement authentication using a custom filter for maximum control or by configuring the gateway as an OAuth2 Resource Server for a more declarative, standard-compliant approach.
- The choice between these patterns is a trade-off between flexibility and simplicity, a key consideration in system design.
Next Up
We've secured our system from external threats by validating tokens at the gateway. But what about the communication between our services? Should the order-service be able to call the inventory-service without any authentication? In our next lesson, we will answer this by exploring strategies for inter-service authentication, such as propagating JWTs or using mTLS.
Can't find a good explanation? Sign up and we'll make it for you
Sign up