Spring Cloud Gateway 怎么编写全局过滤器实现统一鉴权逻辑?

文章导读
Spring Cloud Gateway 实现统一鉴权最标准的做法是实现 GlobalFilter 接口,将其注册为 Bean 后即可对所有路由生效。
📋 目录
  1. A 前置依赖
  2. B 完整代码实现
  3. C 验证方法
  4. D 常见坑与排查
A A

Spring Cloud Gateway 实现统一鉴权最标准的做法是实现 GlobalFilter 接口,将其注册为 Bean 后即可对所有路由生效。

先说结论:通过实现 GlobalFilter 接口并在 filter 方法中拦截请求,适合网关层统一校验 Token 或签名。

  • 适合全局限权场景
  • 需注意 Filter 执行顺序
  • 严禁阻塞操作
  • 需处理 OPTIONS 跨域请求

前置依赖

确保项目中包含 Spring Cloud Gateway 起步依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

完整代码实现

以下代码包含白名单放行、OPTIONS 请求处理、Token 校验及 JSON 错误响应写入,可直接参考使用。

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    // 白名单路径,无需鉴权
    private static final List<String> WHITE_LIST = Arrays.asList(
        "/auth/login", "/actuator/health"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        String method = request.getMethod().name();

        // 1. 放行白名单路径
        if (WHITE_LIST.stream().anyMatch(path::startsWith)) {
            return chain.filter(exchange);
        }

        // 2. 放行跨域预检请求
        if ("OPTIONS".equalsIgnoreCase(method)) {
            return chain.filter(exchange);
        }

        // 3. 获取 Token
        String token = request.getHeaders().getFirst("Authorization");

        // 4. 校验逻辑 (此处替换为实际 JWT 校验)
        if (token == null || !token.startsWith("Bearer ")) {
            return unauthorized(exchange);
        }

        // 5. 鉴权通过,继续链路
        return chain.filter(exchange);
    }

    // 统一返回 401 JSON 错误
    private Mono<Void> unauthorized(ServerWebExchange exchange) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        String body = "{\"code\":401,\"msg\":\"Unauthorized\"}";
        DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
        DataBuffer dataBuffer = bufferFactory.wrap(body.getBytes(StandardCharsets.UTF_8));
        return exchange.getResponse().writeWith(Mono.just(dataBuffer));
    }

    @Override
    public int getOrder() {
        // 优先级高于一般业务过滤器
        return -100;
    }
}

验证方法

1. 无 Token 请求测试

使用 curl 命令访问受保护接口,不带 Authorization 头,预期返回 401 状态码及 JSON body。

Spring Cloud Gateway 怎么编写全局过滤器实现统一鉴权逻辑?
curl -i http://gateway-host/api/protected

2. 有 Token 请求测试

带上合法的 Token 请求,预期返回 200 状态码或后端服务的实际响应。

curl -i -H "Authorization: Bearer valid_token" http://gateway-host/api/protected

3. 跨域请求测试

发送 OPTIONS 请求,预期直接放行返回 200,不被鉴权拦截。

curl -i -X OPTIONS http://gateway-host/api/protected

常见坑与排查

1. 阻塞操作导致性能下降

Spring Cloud Gateway 怎么编写全局过滤器实现统一鉴权逻辑?

GlobalFilter 运行在响应式线程池中,严禁在 filter 方法中进行同步阻塞操作(如 JDBC 查询、HTTP 同步请求)。如果需要调用外部鉴权服务,必须使用 WebClient 等异步客户端。

2. 响应体写入时机

在 WebFlux 中,一旦响应提交(setComplete),就不能再修改响应体。错误信息必须在 writeWith 中写入,并设置正确的 Content-Type 为 application/json。

3. 过滤器顺序冲突

如果项目中存在多个全局过滤器,顺序设置不当可能导致鉴权在日志记录之前执行。发现逻辑不生效时,优先检查 getOrder 返回值。