从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 } }
最后要做的调整
- 移除原有注册代码:删掉原来的
ServletRegistrationBean和FilterRegistrationBean的@Bean配置,因为我们已经用适配器替代了。 - 处理请求体(如果需要):如果你的Filter/Servlet需要读取请求体,一定要先缓存Gateway的请求体(因为WebFlux的请求体是流式的,只能读取一次),再复制到MockHttpServletRequest中。
- Session适配(如果涉及):如果原Filter依赖Servlet Session,需要引入Spring Session的反应式版本(比如
spring-session-core+spring-session-data-redis),实现Session的共享和适配。
内容的提问来源于stack exchange,提问作者vijaya lakshmi
相关产品推荐
相关产品推荐

