How to Build a Spring Cloud Gateway with Dynamic Routing and Auth Filters
This tutorial walks you through setting up a Spring Cloud Gateway service using Spring Boot, configuring static and Nacos‑driven dynamic routes, and implementing a custom authentication filter with Redis token validation, providing a complete beginner‑friendly guide to gateway development.
Preface
This article records how to use Spring Cloud Gateway to build a gateway service and implement dynamic routing, serving as an entry‑level tutorial for those unfamiliar with gateway services.
Service Setup
Framework
SpringBoot 2.1
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>spring-cloud-gateway-core
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
</dependency>common-lang3
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>Route Configuration
The gateway acts as a unified entry point; each route maps to a downstream microservice.
server:
port: 8080
spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix= 1Routes diagram:
Configuration Interpretation
Service demo-server runs at 127.0.0.1:8081, so the route uri is http://localhost:8081.
The gateway listens on port 8080. Accessing localhost:8080/demo-server forwards the request to demo-server.
Calling the downstream API localhost:8081/api/test via the gateway uses the address localhost:8080/demo-server/api/test; the StripPrefix=1 filter removes the first path segment.
Static configuration requires a service restart to take effect, which is undesirable for production. The next section introduces dynamic routing with Nacos.
Dynamic Routing
Deploy a Nacos instance (Docker or source) and store route definitions in Nacos as JSON.
Nacos Configuration
groupId: use the gateway service name dataId: routes Format: JSON
[
{
"id": "xxx-server",
"order": 1, # priority
"predicates": [{
"args": {"pattern": "/xxx-server/**"},
"name": "Path"
}],
"filters": [{
"args": {"parts": 0},
"name": "StripPrefix"
}],
"uri": "http://localhost:8080/xxx-server"
}
]Comparison of JSON and YAML configurations:
{
"id":"demo-server",
"predicates":[{"args":{"pattern":"/demo-server/**"},"name":"Path"}],
"filters":[{"args":{"parts":1},"name":"StripPrefix"}],
"uri":"http://localhost:8081"
} spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix= 1Code Implementation
The core of Nacos dynamic routing is listening to configuration changes and updating the gateway routes via the Spring Cloud API.
@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosDynamicRouteService.class);
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher applicationEventPublisher;
private static List<String> routeIds = Lists.newArrayList();
@NacosConfigListener(dataId = "routes", groupId = "gateway-server")
public void routeConfigListener(String configInfo) {
clearRoute();
try {
List<RouteDefinition> gatewayRouteDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
addRoute(routeDefinition);
}
publish();
LOGGER.info("Dynamic Routing Publish Success");
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
private void clearRoute() {
for (String id : routeIds) {
routeDefinitionWriter.delete(Mono.just(id)).subscribe();
}
routeIds.clear();
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
private void addRoute(RouteDefinition definition) {
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
routeIds.add(definition.getId());
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
private void publish() {
this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
}
}Filters
Gateway provides GlobalFilter and Ordered interfaces for custom filters. GlobalFilter filter() implements filter logic. Ordered getOrder() defines execution order.
Typical gateway filters handle authentication and rate limiting. The following section demonstrates an authentication filter.
Authentication Filter
The filter validates a token stored in Redis and injects the user ID into the request header.
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String TOKEN_HEADER_KEY = "auth_token";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String token = getToken(request);
ServerHttpResponse response = exchange.getResponse();
if (StringUtils.isBlank(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String userId = getUserIdByToken(token);
if (StringUtils.isBlank(userId)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
request = builder.header("user_id", userId).build();
resetTokenExpirationTime(token, userId);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0; // higher priority
}
private String getUserIdByToken(String token) {
String redisKey = String.join(":", "auth_token", token);
return redisTemplate.opsForValue().get(redisKey);
}
private void resetTokenExpirationTime(String token, String userId) {
String redisKey = String.join(":", "auth_token", token);
redisTemplate.opsForValue().set(redisKey, userId, 2, TimeUnit.HOURS);
}
private static String getToken(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(TOKEN_HEADER_KEY);
if (StringUtils.isBlank(token)) {
token = request.getQueryParams().getFirst(TOKEN_HEADER_KEY);
}
if (StringUtils.isBlank(token)) {
HttpCookie cookie = request.getCookies().getFirst(TOKEN_HEADER_KEY);
if (cookie != null) {
token = cookie.getValue();
}
}
return token;
}
}Summary
Gateway can realize routing through configuration; integrating Nacos and a configuration listener enables dynamic routing. Implementing GlobalFilter and Ordered allows quick creation of authentication filters, and the article details the token‑validation flow after login.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
