🌐 Step 2: Gateway Swagger Integration
🧭 Series Navigation:
- ▶️ Spring Documentation Hub
- ▶️ Step 1: SpringDoc OpenAPI Integration
- ✅ Step 2: Gateway Swagger Integration (Current)
- ▶️ Step 3: Advanced Swagger 3 Features
🎥 YouTube Video: Coming Soon – Subscribe to JavaiOS Channel
Swagger UI Integration via Spring Cloud Gateway 🌐
This document explains how Swagger UI is aggregated across multiple microservices behind a Spring Cloud Gateway, detailing the request flow and how security is managed for a unified documentation experience.
In the AdventureTube microservice architecture, it requires a two-phase integration approach to properly aggregate documentation across all services while maintaining security and proper routing.
🧭 Overview
Spring Cloud Gateway serves as the central entry point that aggregates Swagger documentation from multiple microservices into a single, unified interface. This approach provides developers with one place to discover, understand, and test all available APIs across the entire microservice ecosystem.
Key Benefits:
- Centralized Documentation – Single URL for all API documentation
- Unified Testing – Test APIs across different services from one interface
- Consistent Security – Standardized authentication across all documented endpoints
- Service Discovery – Automatic aggregation of new microservice documentation
🛠️ Two-Phase Integration Overview
🔹 Phase 1: Individual Service Configuration
Each microservice is configured with SpringDoc OpenAPI to generate Swagger documentation. These are then aggregated through the Gateway using predefined routes and exposed under a unified Swagger UI.
Reference: Complete Phase 1 implementation in Step 1
🔹 Phase 2: Gateway Security Configuration
After setting up the gateway forwarding, security configurations within each microservice must be adjusted to permit access to documentation endpoints (/v3/api-docs, /swagger-ui/**) while still protecting business logic routes with JWT-based filters.
🌊 Complete Request Flow Analysis
1. Initial Swagger UI Request 🎯
A user accesses the Swagger UI at:
https://api.adventuretube.net/swagger-ui/index.html
This triggers SpringDoc in the gateway-service, using the following configuration:
springdoc:
swagger-ui:
enabled: true
path: /swagger-ui.html
urls:
- name: Auth Service
url: /auth-service/v3/api-docs
- name: Member Service
url: /member-service/v3/api-docs
- name: Web Service
url: /web-service/v3/api-docs
- name: Geospatial Service
url: /geo-service/v3/api-docs
api-docs:
enabled: true
These urls define the destinations Swagger UI will call through the gateway to aggregate documentation from all microservices.
2. Gateway Routing and Security 🔐
In GatewayConfig.java, all primary API routes (e.g., /auth/**, /member/**, etc.) are configured with .filters(f -> f.filter(filter)). This means every incoming request is:
- Intercepted by the global custom
AuthenticationFilter - Forwarded to the appropriate microservice with the
/XXX-serviceprefix stripped
Example: /auth-service/xyz is forwarded as /xyz to auth-service
🔐 RouterValidator Configuration
The AuthenticationFilter uses RouterValidator to determine which endpoints should bypass authentication:
// Open endpoints that bypass JWT authentication
List.of(
"^/auth-service/v3/api-docs.*",
"^/member-service/v3/api-docs.*",
"^/web-service/v3/api-docs.*",
"^/geo-service/v3/api-docs.*",
"^/swagger-ui.*"
)
These paths are considered open endpoints, so while the filter still runs, it allows these requests through without token validation.
3. Route Forwarding Configuration 🚀
Each Swagger documentation route uses .stripPrefix(1) to clean up the path before forwarding:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// Swagger documentation routes
.route("auth-docs", r -> r.path("/auth-service/v3/api-docs")
.filters(f -> f.stripPrefix(1))
.uri("lb://auth-service"))
.route("member-docs", r -> r.path("/member-service/v3/api-docs")
.filters(f -> f.stripPrefix(1))
.uri("lb://member-service"))
.route("web-docs", r -> r.path("/web-service/v3/api-docs")
.filters(f -> f.stripPrefix(1))
.uri("lb://web-service"))
.route("geo-docs", r -> r.path("/geo-service/v3/api-docs")
.filters(f -> f.stripPrefix(1))
.uri("lb://geo-service"))
// Main API routes with authentication
.route("auth-service", r -> r.path("/auth-service/**")
.filters(f -> f.stripPrefix(1).filter(filter))
.uri("lb://auth-service"))
// Additional service routes...
.build();
}
Path Transformation:
- From:
/auth-service/v3/api-docs - To:
/v3/api-docs(inside the target microservice)
🧩 Microservice Swagger Handling
🔐 Auth Service Security Configuration
When the gateway forwards a request to /auth-service/v3/api-docs, the route is stripped to /v3/api-docs and passed to auth-service.
- Spring Security Filter Chain Engagement: The request hits
auth-service‘s Spring Security configuration:
@Bean
@Order(1)
public SecurityFilterChain apiFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.securityMatcher("/auth/**")
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(
"/auth/register",
"/auth/login",
"/auth/refreshToken",
"/auth/logout",
"/swagger-ui.html",
"/swagger-ui/**",
"/v3/api-docs",
"/v3/api-docs/**"
).permitAll()
.anyRequest().hasRole("ADMIN")
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(customAuthenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
Key Security Configurations:
- Open Documentation Endpoints:
/v3/api-docsand/swagger-ui/**are permitted without authentication - Protected Business Logic: All other
/auth/**endpoints require ADMIN role - Stateless Session: No session state maintained for documentation requests
- JWT Filter Integration: Authentication filter runs but permits documentation access
🔄 Complete Flow Diagram
User Request: https://api.adventuretube.net/swagger-ui/index.html
↓
Gateway SpringDoc loads aggregated Swagger UI
↓
Swagger UI requests: /auth-service/v3/api-docs
↓
Gateway AuthenticationFilter (permits documentation endpoints)
↓
Route forwarding: /auth-service/v3/api-docs → /v3/api-docs
↓
Auth Service Spring Security (permits /v3/api-docs)
↓
Auth Service SpringDoc generates OpenAPI specification
↓
Response returns to Gateway → Swagger UI
↓
User sees unified documentation interface
⚙️ Implementation Best Practices
1. Gateway Configuration
@Configuration
public class GatewayConfig {
@Autowired
private AuthenticationFilter filter;
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// Documentation routes (no authentication filter)
.route("auth-docs", r -> r.path("/auth-service/v3/api-docs")
.filters(f -> f.stripPrefix(1))
.uri("lb://auth-service"))
// API routes (with authentication filter)
.route("auth-service", r -> r.path("/auth-service/**")
.filters(f -> f.stripPrefix(1).filter(filter))
.uri("lb://auth-service"))
.build();
}
}
2. Service Discovery Integration
# Gateway application.yml
springdoc:
swagger-ui:
urls:
- name: Auth Service
url: /auth-service/v3/api-docs
- name: Member Service
url: /member-service/v3/api-docs
# Automatically discover services from Eureka
use-management-port: false
disable-swagger-default-url: true
3. CORS Configuration for Documentation
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/v3/api-docs/**", configuration);
source.registerCorsConfiguration("/swagger-ui/**", configuration);
return source;
}
🚨 Common Issues and Solutions
Issue 1: Internal URLs in Documentation
Problem: Swagger UI shows internal service URLs instead of gateway URLs
Solution: Add @OpenAPIDefinition to each microservice:
@OpenAPIDefinition(
servers = {
@Server(url = "https://api.adventuretube.net", description = "AdventureTube API Gateway")
}
)
@SpringBootApplication
public class AuthServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServiceApplication.class, args);
}
}
Issue 2: Authentication Errors for Documentation
Problem: Documentation endpoints require authentication
Solution: Ensure proper security configuration in each service:
// Add to security configuration
.requestMatchers(
"/v3/api-docs",
"/v3/api-docs/**",
"/swagger-ui.html",
"/swagger-ui/**"
).permitAll()
Issue 3: Gateway Route Conflicts
Problem: Documentation routes conflict with API routes
Solution: Order routes correctly – documentation routes before API routes:
return builder.routes()
// Documentation routes first (more specific)
.route("auth-docs", r -> r.path("/auth-service/v3/api-docs")...)
// API routes second (more general)
.route("auth-service", r -> r.path("/auth-service/**")...)
.build();
📊 Performance Considerations
Caching Strategy
# Enable caching for API docs
springdoc:
cache:
disabled: false
api-docs:
resolve-schema-properties: true
# Gateway-level caching
spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 100
max-idle-time: 30s
Load Balancing for Documentation
// Custom load balancer configuration
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder()
.filter(ExchangeFilterFunction.ofRequestProcessor(
clientRequest -> {
// Add custom headers for documentation requests
if (clientRequest.url().getPath().contains("/v3/api-docs")) {
return Mono.just(ClientRequest.from(clientRequest)
.header("Cache-Control", "max-age=300")
.build());
}
return Mono.just(clientRequest);
}
));
}
✅ Implementation Checklist
Gateway Configuration ✅
- ✅ SpringDoc aggregation configuration
- ✅ Route definitions for documentation endpoints
- ✅ Authentication filter bypass for docs
- ✅ CORS configuration for cross-origin requests
Service Security ✅
- ✅ Documentation endpoints permitted in security config
- ✅ JWT filter integration maintained
- ✅ Business logic endpoints protected
- ✅ OpenAPI server URL configuration
Testing & Validation ✅
- ✅ Unified Swagger UI accessible
- ✅ All services documentation loading
- ✅ Authentication working for protected endpoints
- ✅ Documentation endpoints accessible without tokens
🚀 Continue Your Documentation Journey
🧭 Next Steps in Spring Documentation:
- ▶️ Spring Documentation Hub
- ▶️ Step 1: SpringDoc OpenAPI Integration
- ✅ Step 2: Gateway Swagger Integration (Current)
- ▶️ Step 3: Advanced Swagger 3 Features
Part of the AdventureTube technical blog series supporting the JavaiOS YouTube channel.
