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.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How to Build a Spring Cloud Gateway with Dynamic Routing and Auth Filters

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= 1

Routes 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= 1

Code 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaNacosdynamic routingSpring Cloud GatewayAuthentication Filter
Java High-Performance Architecture
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.