Building a Spring Cloud Gateway Service with Dynamic Routing via Nacos and Authentication Filter
This article demonstrates how to set up a Spring Cloud Gateway service using SpringBoot 2.1, configure static routes, implement dynamic routing through Nacos configuration listening, and create a custom authentication GlobalFilter, providing a step‑by‑step guide with Maven dependencies, YAML/JSON examples, and full Java code.
The article explains how to build a gateway service with Spring Cloud Gateway, starting from the required Maven dependencies and the basic SpringBoot 2.1 parent configuration.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>It adds the spring-cloud-gateway-core and commons-lang3 dependencies needed for routing and utility functions.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>Route Configuration
The static route is defined in application.yml (or application.yaml ) with fields such as id , uri , predicates , and filters . The example routes requests with path /demo-server/** to a backend service running at http://localhost:8081 and strips the first path segment.
server:
port: 8080
spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix=1A table describes each configuration item (id, uri, predicates, filters) and their meanings.
Dynamic Routing with Nacos
To avoid restarting the gateway when routes change, the article shows how to store route definitions in Nacos. The Nacos dataId is routes and the content is a JSON array of RouteDefinition objects.
[
{
"id": "xxx-server",
"order": 1,
"predicates": [{"name": "Path", "args": {"pattern": "/xxx-server/**"}}],
"filters": [{"name": "StripPrefix", "args": {"parts": 0}}],
"uri": "http://localhost:8080/xxx-server"
}
]The JSON structure matches the YAML representation, making it easy to convert between the two formats.
Code Implementation for Dynamic Routing
A Spring component NacosDynamicRouteService implements ApplicationEventPublisherAware and listens to Nacos changes via @NacosConfigListener . When the configuration changes, it clears existing routes, parses the new JSON into RouteDefinition objects, saves them with RouteDefinitionWriter , and publishes a RefreshRoutesEvent to make the changes effective.
@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
@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 methods omitted for brevity
}Custom Authentication Filter
The gateway can enforce authentication by implementing a GlobalFilter and Ordered . The filter extracts a token from the request header, query parameter, or cookie, validates it against Redis, refreshes its expiration, and adds a user_id 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();
}
request = exchange.getRequest().mutate().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;
}
}Conclusion
The guide shows that Spring Cloud Gateway can be configured statically via YAML or dynamically via Nacos, and that custom filters such as authentication can be added easily by implementing the provided interfaces. The full code snippets and configuration examples enable readers to quickly set up a production‑ready gateway.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.