You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Zuul迁移至Spring Cloud Gateway后原生Servlet Filter未触发的解决方案咨询

解决方案:适配Servlet Filter到Spring Cloud Gateway

这问题我遇过,核心原因得先理清楚:Spring Cloud Gateway是基于WebFlux反应式框架的,默认跑在Netty容器上,完全不依赖Servlet API。你原来的OAuthFilter是基于HttpServlet的Filter,用FilterRegistrationBean注册的方式只对Servlet容器生效,Gateway根本不会走这条过滤链,所以自然不会触发。

因为你不能修改原OAuthFilter的代码,我们可以通过适配器模式把Servlet组件转换成Gateway支持的组件,下面是具体可行的方案:

方案1:将Servlet Filter适配为GlobalFilter

写一个适配器类,实现Gateway的GlobalFilter和Ordered接口,在里面调用原OAuthFilter的doFilter方法,同时处理WebFlux与Servlet请求响应的转换:

@Component
public class OAuthFilterAdapter implements GlobalFilter, Ordered {

    private final OAuthFilter oAuthFilter;

    // 注入原Filter实例
    public OAuthFilterAdapter(OAuthFilter oAuthFilter) {
        this.oAuthFilter = oAuthFilter;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 模拟Servlet请求响应对象
        MockHttpServletRequest servletRequest = new MockHttpServletRequest();
        MockHttpServletResponse servletResponse = new MockHttpServletResponse();

        // 复制Gateway请求的核心信息到Servlet请求
        copyRequestDetails(exchange.getRequest(), servletRequest);

        try {
            // 调用原Filter的doFilter方法,在FilterChain回调中继续Gateway的过滤链
            oAuthFilter.doFilter(servletRequest, servletResponse, request -> {
                chain.filter(exchange).subscribe();
            });
        } catch (ServletException | IOException e) {
            return Mono.error(e);
        }

        // 将Servlet响应的结果同步回Gateway响应
        copyResponseDetails(servletResponse, exchange.getResponse());

        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {
        // 和原Filter的优先级保持一致,确保执行顺序正确
        return Ordered.HIGHEST_PRECEDENCE;
    }

    // 复制请求头、方法、路径等信息
    private void copyRequestDetails(ServerHttpRequest gatewayRequest, MockHttpServletRequest servletRequest) {
        gatewayRequest.getHeaders().forEach((key, values) -> {
            values.forEach(value -> servletRequest.addHeader(key, value));
        });
        servletRequest.setMethod(gatewayRequest.getMethodValue());
        servletRequest.setRequestURI(gatewayRequest.getPath().value());
        
        // 如果Filter需要读取请求体,这里要额外处理(WebFlux请求体只能读一次,需缓存)
        // 示例:byte[] body = gatewayRequest.getBody().blockFirst().asByteBuffer().array();
        // servletRequest.setContent(body);
    }

    // 复制响应头、状态码等信息
    private void copyResponseDetails(MockHttpServletResponse servletResponse, ServerHttpResponse gatewayResponse) {
        servletResponse.getHeaderNames().forEach(headerName -> {
            gatewayResponse.getHeaders().add(headerName, servletResponse.getHeader(headerName));
        });
        gatewayResponse.setStatusCode(HttpStatus.valueOf(servletResponse.getStatus()));
    }
}

方案2:将Servlet适配为Gateway端点

你的OAuthCodeRecieverServlet同样无法在Gateway中生效,需要把它适配成Spring MVC的Controller端点(Gateway支持Spring MVC的注解式端点):

@RestController
public class OAuthCodeReceiverController {

    private final OAuthCodeRecieverServlet oAuthCodeRecieverServlet;

    public OAuthCodeReceiverController(OAuthCodeRecieverServlet oAuthCodeRecieverServlet) {
        this.oAuthCodeRecieverServlet = oAuthCodeRecieverServlet;
    }

    @RequestMapping("/login/oauth/codereceiver")
    public void handleCodeReceiver(ServerHttpRequest gatewayRequest, ServerHttpResponse gatewayResponse) throws ServletException, IOException {
        MockHttpServletRequest servletRequest = new MockHttpServletRequest();
        MockHttpServletResponse servletResponse = new MockHttpServletResponse();

        // 复制请求信息
        copyRequestDetails(gatewayRequest, servletRequest);
        
        // 调用原Servlet的service方法
        oAuthCodeRecieverServlet.service(servletRequest, servletResponse);

        // 同步响应结果到Gateway
        copyResponseDetails(servletResponse, gatewayResponse);
        gatewayResponse.getOutputStream().write(servletResponse.getContentAsByteArray());
    }

    // 复用上面的copyRequestDetails和copyResponseDetails方法
    private void copyRequestDetails(ServerHttpRequest gatewayRequest, MockHttpServletRequest servletRequest) {
        // 实现同方案1
    }

    private void copyResponseDetails(MockHttpServletResponse servletResponse, ServerHttpResponse gatewayResponse) {
        // 实现同方案1
    }
}

最后要做的调整

  1. 移除原有注册代码:删掉原来的ServletRegistrationBean和FilterRegistrationBean的@Bean配置,因为我们已经用适配器替代了。
  2. 处理请求体(如果需要):如果你的Filter/Servlet需要读取请求体,一定要先缓存Gateway的请求体(因为WebFlux的请求体是流式的,只能读取一次),再复制到MockHttpServletRequest中。
  3. Session适配(如果涉及):如果原Filter依赖Servlet Session,需要引入Spring Session的反应式版本(比如spring-session-core+spring-session-data-redis),实现Session的共享和适配。

内容的提问来源于stack exchange,提问作者vijaya lakshmi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 09:49:07