Building a Spring Cloud Gateway Service with Dynamic Routing Using Nacos
This tutorial demonstrates how to set up a Spring Cloud Gateway service with static route configuration, then extend it to support dynamic routing through Nacos, and finally implement custom authentication and global filters using Java and Spring Boot.
The article begins with a brief introduction explaining the purpose of the guide: to help beginners quickly build a gateway service using Spring Cloud Gateway, understand route configuration, authentication flow, and dynamic routing.
Service Setup
It specifies the required framework versions (Spring Boot 2.1) and lists Maven dependencies for spring-boot-starter-parent , spring-cloud-gateway-core , and commons-lang3 .
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>Static Route Configuration
The gateway runs on port 8080 and forwards requests to a demo service at http://localhost:8081 using a YAML route definition with a Path=/demo-server/** predicate and a StripPrefix=1 filter.
server:
port: 8080
spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix= 1It explains how requests are matched and routed, and notes that changing static routes requires a service restart.
Dynamic Routing with Nacos
To avoid restarts, the guide introduces Nacos as a configuration center. It describes deploying Nacos (e.g., via Docker) and storing route definitions in a JSON dataId named routes under the group gateway-server .
[
{
"id": "xxx-server",
"order": 1,
"predicates": [{
"args": {"pattern": "/xxx-server/**"},
"name": "Path"
}],
"filters": [{
"args": {"parts": 0},
"name": "StripPrefix"
}],
"uri": "http://localhost:8080/xxx-server"
}
]It compares the JSON format with the equivalent YAML configuration.
Dynamic Route Service Implementation
The NacosDynamicRouteService class listens to Nacos configuration changes using @NacosConfigListener , parses the JSON into RouteDefinition objects, clears existing routes, adds new ones, and publishes a RefreshRoutesEvent to make them effective.
@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
routeIds = Lists.newArrayList();
@NacosConfigListener(dataId = "routes", groupId = "gateway-server")
public void routeConfigListener(String configInfo) {
clearRoute();
try {
List
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);
}
}
// clearRoute, addRoute, publish, setApplicationEventPublisher methods omitted for brevity
}Custom Global Filter and Authentication
The guide shows how to create a global filter by implementing GlobalFilter and Ordered . The AuthFilter extracts a token from headers, query parameters, or cookies, validates it against Redis, returns 401 for missing or invalid tokens, and injects the user ID into the request header for downstream services.
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Autowired
private RedisTemplate
redisTemplate;
private static final String TOKEN_HEADER_KEY = "auth_token";
@Override
public Mono
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; }
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;
}
}It also adds the Redis dependency and configuration needed for token storage.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency> spring:
redis:
host: redis-server
port: 6379
password:
database: 0Conclusion
The article summarizes that Spring Cloud Gateway can handle static routing via configuration, achieve dynamic routing by integrating Nacos and listening to configuration changes, and extend functionality with custom global filters such as authentication, providing a complete beginner-friendly guide.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.