如何让Zuul代理处理跨域?配置后仍报403问题排查
解决Zuul网关下跨域403问题的正确姿势
你遇到的问题很典型:把跨域配置从微服务移到Zuul后没生效,核心原因有两个:Zuul默认的Spring MVC跨域配置没生效,以及微服务依然在校验外部请求的Origin头。下面一步步帮你解决:
1. 先让Zuul真正处理跨域请求
你之前用WebMvcConfigurerAdapter配置跨域,对Zuul来说可能不生效——因为Zuul的请求流转是通过ZuulFilter,而非Spring MVC的DispatcherServlet。正确的做法是用CorsFilter来配置Zuul的跨域:
@Configuration public class ZuulCorsConfig { @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); // 允许前端携带Cookie等凭证(如果需要的话) config.setAllowCredentials(true); // 生产环境建议替换成你的前端域名,比如"https://your-frontend.com" config.setAllowedOrigins(Collections.singletonList("*")); // 允许的请求头,覆盖常见场景 config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With")); // 允许的HTTP方法 config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 暴露给前端的响应头(比如自定义的token头) config.setExposedHeaders(Arrays.asList("Authorization")); source.registerCorsConfiguration("/api/**", config); // 对应你的Zuul前缀/api return new CorsFilter(source); } }
这个过滤器会在Zuul层面直接处理跨域预检请求(OPTIONS),并给响应添加正确的CORS头,避免请求到达微服务时触发跨域校验。
2. 让微服务识别并信任来自Zuul的请求
现在Zuul已经处理了跨域,但微服务收到的请求里依然带有前端的Origin头,它会认为这是外部直接请求,所以返回403。我们需要给Zuul转发的请求加一个“内部标识”,让微服务信任这些请求:
第一步:给Zuul添加请求头过滤器
创建一个Zuul前置过滤器,给转发的请求添加自定义头:
@Component public class InternalRequestMarkerFilter extends ZuulFilter { @Override public String filterType() { return "pre"; // 在请求转发前执行 } @Override public int filterOrder() { return 1; // 优先级尽量高,确保头被添加 } @Override public boolean shouldFilter() { return true; // 对所有请求生效 } @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); // 添加自定义头,标识这是Zuul转发的内部请求 ctx.addZuulRequestHeader("X-Internal-Proxy", "Zuul"); return null; } }
第二步:微服务端校验内部标识
修改微服务的CORS配置,只对没有这个自定义头的请求做跨域限制(或者直接拒绝外部直接请求):
方案A:通过过滤器拒绝外部直接调用
如果不允许微服务被直接访问,直接加一个过滤器拦截无标识的请求:
@Configuration public class MicroserviceSecurityConfig { @Bean public FilterRegistrationBean<Filter> internalRequestValidationFilter() { FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String internalProxyHeader = req.getHeader("X-Internal-Proxy"); // 如果没有自定义头,直接返回403 if (internalProxyHeader == null || !"Zuul".equals(internalProxyHeader)) { HttpServletResponse res = (HttpServletResponse) response; res.sendError(HttpServletResponse.SC_FORBIDDEN, "Direct access to microservice is not allowed"); return; } chain.doFilter(request, response); } }); registrationBean.addUrlPatterns("/*"); return registrationBean; } // 如果你还是需要允许特定场景的直接调用,可以保留基础CORS配置,否则可以移除之前的CorsConfig @Override public void addCorsMappings(CorsRegistry registry) { // 只允许内部测试域名或者Zuul的内部地址 registry.addMapping("/**") .allowedOrigins("http://zuul-internal:8080") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"); } }
方案B:修改CORS配置信任Zuul
如果需要允许微服务被内部其他服务调用,可以修改CORS配置,允许Zuul的内部地址:
@Configuration public class MicroserviceCorsConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 替换成你的Zuul内部访问地址(比如容器内域名或IP) .allowedOrigins("http://my-zuul-gateway:8080") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true); } }
3. 额外注意事项
- 生产环境不要用
*作为AllowedOrigins:一定要指定具体的前端域名,避免安全风险。 - Spring Security兼容性:如果微服务用了Spring Security,需要确保它允许OPTIONS请求,并且不拦截CORS相关的头。可以在Security配置里添加:
@Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS).permitAll() // 其他配置... } - Zuul前缀配置:确保你的Zuul前缀
/api和CORS配置的路径匹配,避免遗漏。
这样配置后,前端的跨域请求会由Zuul处理,微服务只会接受来自Zuul的请求,外部直接调用微服务会被拒绝,完全符合你的设计预期。
内容的提问来源于stack exchange,提问作者Phate
相关产品推荐
相关产品推荐

