Authentication at the Gateway
Check the Token Once, at the Edge
Instead of every service parsing JWTs, the gateway validates the token and rejects bad requests with 401 early. JWTs, login and OAuth2 are covered in the Spring Security material; here we only wire it into the gateway. Add spring-boot-starter-oauth2-resource-server (reactive security is used automatically because the gateway runs on WebFlux):
@Configuration
@EnableWebFluxSecurity
class GatewaySecurityConfig {
@Bean
SecurityWebFilterChain gatewaySecurity(ServerHttpSecurity http) {
return http
.csrf(ServerHttpSecurity.CsrfSpec::disable) // stateless JSON API
.authorizeExchange(ex -> ex
.pathMatchers("/api/auth/**").permitAll() // login, register
.pathMatchers(HttpMethod.GET, "/api/products/**", "/api/categories/**").permitAll()
.pathMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
.anyExchange().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()))
.build();
}
}spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.shopease.in/realms/shopease # Keycloak realm, or your auth serverPass the User to the Services
After validation, the gateway can forward the user ID in a header so downstream services do not have to parse the token. It must first remove any X-User-Id the client sent, or anyone could pretend to be another user:
@Component
class UserIdHeaderFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerWebExchange cleaned = exchange.mutate()
.request(r -> r.headers(h -> h.remove("X-User-Id"))) // never trust the client
.build();
return cleaned.getPrincipal()
.filter(JwtAuthenticationToken.class::isInstance)
.cast(JwtAuthenticationToken.class)
.map(auth -> cleaned.mutate()
.request(r -> r.header("X-User-Id", auth.getToken().getSubject()))
.build())
.defaultIfEmpty(cleaned)
.flatMap(chain::filter);
}
@Override
public int getOrder() { return 0; }
}Example
curl -i -X POST localhost:8080/api/orders -d @order.json -H "Content-Type: application/json"
curl -i -X POST localhost:8080/api/orders -d @order.json -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI0MiIsInNjb3BlIjoidXNlciJ9.kQ..."Example output
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
HTTP/1.1 201 Created
{"orderId":1002,"status":"CREATED","total":1499.00}Defence in Depth
Note: The gateway check is not the only check. Services should not be reachable from the internet (private network only), and sensitive services like payment-service should verify the token or a service-to-service credential too. This is covered in the advance level and in the Spring Security material.