How to Build a Spring Cloud Gateway with Dynamic Nacos Routing and Auth Filters
This guide walks through setting up a Spring Cloud Gateway service using Spring Boot 2.1, configuring static and dynamic routes via YAML and Nacos JSON, and implementing a custom authentication filter with Redis token validation, covering gateway framework, route definitions, dynamic updates, and security filters.
Preface
This article records how I use Spring Cloud Gateway to build a gateway service and achieve dynamic routing, helping readers quickly set up a gateway, understand routing configuration, authentication flow, and business handling. It is suitable as an introductory 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 corresponds to a business system entry. By defining routing rules, requests are matched to the appropriate micro‑service.
server:
port: 8080
spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix= 1routes
Interpretation of Configuration
There is a service demo-server deployed locally 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 triggers the Path=/demo-server/** predicate, routing the request to the demo server.
Direct access to the demo server is localhost:8081/api/test. Through the gateway the address becomes localhost:8080/demo-server/api/test. The StripPrefix=1 filter removes the first path segment, i.e., demo-server.
Using Gateway configuration files to set up routing is convenient, but changes require a service restart, which can cause downtime. The following sections show how to achieve dynamic routing with Nacos.
Dynamic Routing
To implement dynamic routing, we combine Nacos with the gateway server. First, deploy a Nacos instance (Docker or source). Refer to the official documentation for details.
Nacos Configuration
groupId: use the gateway service name.
dataId: routes
Configuration format: JSON
[
{
"id": "xxx-server",
"order": 1, // priority
"predicates": [{ // routing predicates
"args": {
"pattern": "/xxx-server/**"
},
"name": "Path"
}],
"filters": [{ // filter rules
"args": {
"parts": 0 // for k8s internal access, set 0
},
"name": "StripPrefix" // start index to strip
}],
"uri": "http://localhost:8080/xxx-server" // target address
}
]The JSON configuration corresponds to the YAML format; understanding both representations is necessary.
JSON vs YAML Comparison
{
"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
Nacos dynamic routing relies on configuration listening; when the configuration changes, the gateway API creates or updates routes.
@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 defining filters. Implementing these interfaces allows custom filter logic.
Typical gateway filters include authentication (login check, blacklist, whitelist) and rate limiting (IP limits). Below is a simple authentication filter implementation.
Authentication Filter
The authentication filter validates the token's effectiveness. The gateway and business services share the same Redis instance.
Add Redis dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>Redis configuration:
spring:
redis:
host: redis-server
port: 6379
password:
database: 0Authentication filter implementation:
@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();
}
// Add user_id header for downstream services
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
request = builder.header("user_id", userId).build();
// Extend token expiration
resetTokenExpirationTime(token, userId);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0; // highest 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;
}
}Conclusion
Gateway can achieve routing through configuration items; integrating Nacos and configuration listeners enables dynamic routing. Implementing GlobalFilter and Ordered interfaces allows quick creation of filters. The article also details the authentication 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.
