How to Build a Spring Cloud Gateway with Dynamic Routing via Nacos
Learn step‑by‑step how to set up a Spring Cloud Gateway service, configure static routes, enable dynamic routing using Nacos, and implement authentication filters with Redis, providing a complete guide for building a robust backend gateway in Java.
Introduction
This article explains how to use Spring Cloud Gateway to build a gateway service, configure static routes, and achieve dynamic routing with Nacos, while also covering authentication flow and filter implementation for beginners.
Dependencies
<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 acts as a unified entry point; each route maps to a downstream microservice. A typical YAML configuration looks like this:
server:
port: 8080
spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix=1Configuration Item
Description
id
Unique route identifier, usually the service name
uri
Target address of the downstream service
predicates
Route matching rules
filters
Filter rules applied to the route
Dynamic Routing with Nacos
By integrating Nacos, routes can be updated without restarting the gateway. Deploy a Nacos instance (Docker or binary) and store route definitions as JSON.
[{
"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 equivalent YAML configuration is:
spring:
cloud:
gateway:
enabled: true
routes:
- id: demo-server
uri: http://localhost:8081
predicates:
- Path=/demo-server/**
filters:
- StripPrefix=1Dynamic Route Service Implementation
@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));
}
}Authentication Filter
The gateway can enforce authentication by implementing a GlobalFilter and Ordered filter that validates a token stored in Redis.
@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;
}
}Redis Dependency and Configuration
<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
Gateway can realize routing through configuration, and integrating Nacos enables dynamic route updates without service downtime. Implementing GlobalFilter and Ordered interfaces allows quick creation of authentication filters, and the article also details the token validation process using Redis.
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 Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
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.
