背景:项目比较紧急,所以用图片+页面的形式写了个小东西,要实现填写信息和购买,可是在调用过程中遇到了跨域问题,如何解决。
问题重现:最初的调用方案是通过gateway路由分发到微服务的web层,再通过feign发起通过Gateway分发调用到服务。最开始报的是跨域问题于是开始着手解决
问题1.页面报错报的是。
我给出了第一个解决方案。
方案一(不适用于Gateway和与tomcat有冲突的)
//重点 @WebFilter(filterName = "corsFilter", urlPatterns = {"/*"}) public class CORSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("初始化filter=========================="); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; response.setHeader("Access-Control-Allow-Origin", "*"); // 这里最好明确的写允许的域名 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization,ybg"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("filter=========================="); } @Override public void destroy() { System.out.println("销毁filter=========================="); }
是让Access-Control-Allow-Origin响应的时候存在response头部。可是这时候遇见了一个很大的坑,至少目前没有找到解决方案那就是gateway的包是包含了web包的,所以在gateway的工程依赖需要web的时候要剔除掉tomcat,所以这个时候第一种方案就不行了,因为没有HttpServletResponse的支持了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
方案二(不适用于Gateway)
这个时候只能找第二种方案
@Component public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }
通过第二种方案意思是我将返回的跨域问题全部忽略,科室这个时候出现了第二个问题
Failed to load http://xx.com/mobile/service: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. Origin 'http://localhost:8081' is therefore not allowed access.
似乎跨域问题解决了,但是这里解决了两次,所以报错,貌似有点眉目了,我就随意在网上搜索了一下果然有这个问题,gateway的自带bug。要剔除其中的一部分问题就解决了
方案三
@Configuration public class CorsConfiguration { private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN,token,username,client"; private static final String ALLOWED_METHODS = "*"; private static final String ALLOWED_ORIGIN = "*"; private static final String ALLOWED_EXPOSE = "*"; private static final String MAX_AGE = "3600"; @Bean public WebFilter corsFilter() { return (ServerWebExchange ctx, WebFilterChain chain) -> { ServerHttpRequest request = ctx.getRequest(); if (CorsUtils.isCorsRequest(request)) { ServerHttpResponse response = ctx.getResponse(); HttpHeaders headers = response.getHeaders(); headers.set("Access-Control-Allow-Origin", ALLOWED_ORIGIN); headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS); headers.add("Access-Control-Max-Age", MAX_AGE); headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS); headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE); headers.add("Access-Control-Allow-Credentials", "true"); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(ctx); }; } }
@Component("corsResponseHeaderFilter") public class CorsResponseHeaderFilter implements GlobalFilter, Ordered { @Override public int getOrder() { // 指定此过滤器位于NettyWriteResponseFilter之后 // 即待处理完响应体后接着处理响应头 return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.defer(() -> { exchange.getResponse().getHeaders().entrySet().stream() .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1)) .filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS))) .forEach(kv -> { kv.setValue(new ArrayList<String>() {{ add(kv.getValue().get(0)); }}); }); return chain.filter(exchange); })); }
通过这两个配置,将完美的解决了Gateway的配置一次,却提示两次跨域的问题。
此博客的最终解决方案鸣谢:
持盾的紫眸 https://blog.csdn.net/zimou5581/article/details/90043178
更多技术问题请加入QQ群:260292706交流