背景:

前端代码要做微前端,我们代码适配了的别人的基座,这个时候所有请求都跨域了


出现的问题:

前端请求跨域,具体显现就不描述了,跨域出现的现象一大把
并且cloud的gateway服务没有配置跨域操作,但是gateway后面的微服务配置了跨域,这样就需要gateway不仅要配置允许跨域,还要屏蔽掉后面微服务的跨域配置(这个后面讲为何)


处理步骤:

Gateway配置跨域

cloud配置跨域,这个简单,网上有很多方法,我记录下对于我来说验证通过的一种方法,直接yaml里面加上如下配置。。

spring:
    cloud:
      gateway:
        globalcors:
          corsConfigurations:
            '[/**]':
              allowedOriginPatterns: "*"
              allowedMethods: "*"
              allowed-headers: "*"
              allowCredentials: true

好了,上面解决了配置跨域问题,紧接着出现一个新的问题,刚才说的后端微服务已经配置了跨域,这就导致前端收到请求的时候,access-control-allow-origin: http://y.jd.com,* 或者证书是两个ture。


要么所有后端微服务都去掉跨域配置,要么找到解决方式,将后端服务配置的跨域给替换掉。
其实我是打算所有微服务改造,但是有个大神建议我在gateway拦截器里面搞事情,处理掉response里面的跨域配置。

虽然我内心是拒绝的,但是为了成为一个有追求的coder,我决定尝试一下

Gateway添加过滤器:

其实gateway已经有一个resquest的过滤器了,现在要操作response,尝试各种方式,最终方案如下:

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

import static org.springframework.http.HttpHeaders.*;


@Component
public class ResponseFilter implements GlobalFilter, Ordered, InitializingBean {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {

            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

                    try {
                        HttpHeaders headers = originalResponse.getHeaders();

                        String requestUrl = exchange.getRequest().getURI().getPath();
                        List<String> origin = exchange.getRequest().getHeaders().get(ORIGIN);

                        if(origin!=null && origin.size()>0){

                            List<String> origins = new ArrayList<>();
                            origins.add(origin.get(0));
                            //Access-Control-Allow-Origin回填,覆盖
                            headers.put(ACCESS_CONTROL_ALLOW_ORIGIN,origins);
                        }

						//Access-Control-Allow-Credentials回填,覆盖
                        List<String> credentials = new ArrayList<>();
                        credentials.add("true");
                        headers.put(ACCESS_CONTROL_ALLOW_CREDENTIALS, credentials);


                    } catch (Exception e){
                        logger.error("++++++++++++++++++++++++=log headers error", e);
                    }

                return super.writeWith(body);
            }
        };

		//这里别忘了build方法的response方法回填
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        logger.info("ResponseBodyFilter: [{}]");
    }

    @Override
    public int getOrder() {
    	//response的fileter一定要小于-1,具体原因忘了。
        return -2;
    }
}

这样就覆盖掉了gateway后面微服务的跨域配置。瞬间感觉自己萌萌哒!


本来以为事情就结束了,换了一个环境部署以后,发现不生效。依然跨域,我真是fffffffffffuck。

istio配置rbac策略,允许http请求类型

在排查这个问题之前,发现options请求是403,post是跨域,我就按照跨域问题排查:
尝试修改我的gateway配置,以为是gateway的错误,改了好多版本都不生效
然后打印日志,发现都没有进入gateway,我才想着去上游看看

没办法,从头开始定位问题,从网管开始,发现网管日志报错,重点注意报错 rbac_access_denied_matched_policy[none]

[2022-08-03T14:23:04.131Z] "OPTIONS /gw/usercenter/sys/query/user HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "ip " "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" "ba2a6902-6d98-9999-af12-85c3394e25bc" "mlops-test.jd.com" "-" - - ip2:8080 ip3:43921 - -

根据ip找了一下pod和svc,发现是istio网管 cluster-local-gateway的ip,也就是还没有我的gateway服务就被拦截,,

面向百度编程嘛,查查查,最后发现是istio的AuthorizationPolicy配置问题

springcloud 迁移 Service Mesh springcloud改造成istio_spring


添加上,瞬间就好了。配一张简单手绘图:

springcloud 迁移 Service Mesh springcloud改造成istio_跨域_02

总结

1、OPTIONS请求失败,就会导致post请求报错跨域。
2、GW是个好东西,又是个经常扯我们蛋的工具,像上面的问题,如果没有GW,我就没有这些问题了。但是它有给我很大方便,例如认证,我只需要在GW里面做,后面服务就不用管了,当然认证被破解导致后面服务的危险是认证的问题,和GW无关
3、碰到问题,理清楚思路,从入口开始往下一步一步排查,例如istio的配置,我找了一圈,没有找到原因,才想着回头看istio网管日志的,如果早点看,可能早就解决了。