🔥Securing Microservices with JWT, Spring Security, and Fault Tolerance Using Spring Cloud Gateway

A cup of JAVA coffee with NeeSri
3 min readSep 1, 2024

--

In a microservices architecture, security and reliability are paramount. This blog will walk you through implementing JWT (JSON Web Token) authentication, using Spring Security to protect your microservices, and adding fault tolerance with Spring Cloud Gateway.

Overview

  • JWT Authentication: Secure your APIs by issuing and validating tokens.
  • Spring Security: Protect endpoints and handle authentication.
  • Spring Cloud Gateway: Route requests, apply filters, and ensure fault tolerance.

Step 1: Setting Up Your Spring Boot Project

Start by setting up a Spring Boot project with the necessary dependencies.

Add Dependencies

In your pom.xml, include the following dependencies:

<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>

<!-- JWT for token generation and validation -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>

<!-- Spring WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<!-- Resilience4j for fault tolerance -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
</dependency>
</dependencies>

Step 2: JWT Utility Class

JWT is used to securely transmit information between parties as a JSON object. Create a utility class for generating and validating tokens.

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtUtil {

private String secretKey = "mySecretKey"; // Use a secure key

// Generate a JWT token
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}

// Validate the JWT token
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}

// Extract the username from the token
public String extractUsername(String token) {
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
return claims.getSubject();
}
}

Step 3: Configuring Spring Security

Spring Security is crucial for securing your endpoints. Below is a basic configuration to protect your APIs.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
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.csrf().disable()
.authorizeExchange()
.pathMatchers("/login", "/token").permitAll() // Allow public access to login and token endpoints
.anyExchange().authenticated() // Protect all other endpoints
.and().oauth2Login(); // Add OAuth2 login if needed
return http.build();
}
}

Step 4: Implementing JWT Authentication Filter

Use a global filter in Spring Cloud Gateway to intercept requests and validate the JWT before forwarding them to downstream services.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {

@Autowired
private JwtUtil jwtUtil;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
HttpHeaders headers = exchange.getRequest().getHeaders();
String authHeader = headers.getFirst(HttpHeaders.AUTHORIZATION);

if (authHeader == null || !authHeader.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

String token = authHeader.substring(7);

if (!jwtUtil.validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

String username = jwtUtil.extractUsername(token);
exchange.getRequest().mutate().header("X-Authenticated-User", username).build();

return chain.filter(exchange);
}

@Override
public int getOrder() {
return -1; // Ensures this filter is executed first
}
}

Step 5: Adding Fault Tolerance with Resilience4j

Fault tolerance is critical in microservices. We will use Resilience4j for this purpose. It provides Circuit Breaker, Retry, Rate Limiter, and other patterns.

Gateway Route Configuration with Fault Tolerance

Configure routes in application.yml with fault tolerance

spring:
cloud:
gateway:
routes:
- id: service1
uri: lb://SERVICE1
filters:
- JwtAuthenticationFilter
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY, GATEWAY_TIMEOUT
predicates:
- Path=/service1/**

Step 6: Creating a Fallback Mechanism

Create a fallback endpoint to handle cases when a service is down or unreachable.

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

@RestController
public class FallbackController {

@GetMapping("/fallback")
public String fallback() {
return "Service is temporarily unavailable. Please try again later.";
}
}

tep 7: Testing the Implementation

  1. Generate a JWT Token: Use the JwtUtil class to generate a token.
  2. Send Requests to the Gateway: Include the JWT in the Authorization header.
  3. Test Fault Tolerance: Simulate service failures to see the circuit breaker and retry mechanisms in action.

Conclusion

By integrating JWT with Spring Security and Spring Cloud Gateway, you create a secure and resilient microservices architecture. The addition of Resilience4j ensures that your services remain reliable even under failure conditions. This setup not only secures your APIs but also provides a robust mechanism to handle failures gracefully.

This comprehensive approach helps you build a production-ready microservices architecture that is both secure and fault-tolerant.

Thanks & Happy Learning… :)

--

--

No responses yet