2020再谈跨域

跨域这个话题已经谈了不少年了,怎么如今又要谈这个问题?原本是能够没必要再提了的,可是因为Chrome 86版本之后又增长了不少限制,致使咱们不得再也不次提起。html

CORS

对于前端开发来讲,跨域一般有两种方式,一种是在服务端修改nginx配置,在response headers里添加CORS设置,另外一种是在本地架设代理。咱们先谈第一种。前端

本来在nginx里添加CORS已是一种常规操做,简单到无以复加:node

location /somewhere/ {
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin "$http_origin";
            add_header Access-Control-Allow-Credentials "true";
            add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
            add_header Access-Control-Allow-Headers "sitessubid,Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 200;
        }
        if ($request_method = POST) {
            add_header Access-Control-Allow-Origin "$http_origin";
            add_header Access-Control-Allow-Credentials "true";
        }
    }

而后咱们只要在每一个axios或者fetch请求里添加withCredentials就能自动把对应该服务器的cookies随请求一并发送了。react

但问题在Chrome 86版本之后,Chrome强制给从服务端下发的每一个cookie都增长了一个缺省的SameSite属性,而且强制把这个属性设置为Lax,从而致使咱们的withCredentials不起做用,只要是跨域的状况,cookies连取都取不到,更不要提发送了。在这里我不想再详述SameSite的原理,感兴趣的能够去这里了解。ios

这就须要咱们必须从服务端入手,修改nginx下发cookies时的SameSite属性。nginx

SameSite和secure

修改SameSite又分为两种状况:若是你用的是低版本的nginx,可能还不必定能彻底解决问题,目前惟一能修改的变通之道是修改cookiepath,使它后面带上SameSite属性,例如这样:git

proxy_cookie_path ~(.*) "$1; SameSite=none";

这里实际修改的是cookie里的path值,但因为附加了;,因此浏览器会认为自;之后的是新属性,因此也会接受这个SameSite设定。axios

可是仅此还不够,Chrome又要求若是SameSite值为none的话,则还必须设置secure属性,也就是要求全部下发cookie的域名必须使用https协议,因此最终修改的结果是:跨域

proxy_cookie_path ~(.*) "$1; SameSite=none; secure";

但这种状况只能解决相似于cookie是以path结尾的状况,若是上游服务器下发cookie时不止有path,而且在path结尾指定了其它的SameSite,那就无能为力了,由于这么修改path以后cookie会变成:浏览器

path=/; SameSite=none; secure; SameSite=Lax

浏览器仍然以最后一个SameSite=Lax为准。目前低版本nginx对这个问题无解。

但若是你是nginx 1.19.3及以上,新增了一个属性,能够更好地解决这个问题:

proxy_cookie_flags one samesite=none;

添加secure标志的方法相似,参考官方文档,不赘述。

localhost的https

由上可知,咱们能够经过修改nginx的方法来改造跨域cookies的发送。但这时若是你又不想跨域了,还想用本地代理的方式来解决,又会遇到一个问题:由于咱们上面在下发cookies的时候,缺省地给每一个cookie都带上了secure属性,这就致使咱们在使用本地代理的时候反而没法设置cookies,由于咱们本地的开发服务器每每使用的是相似于http://localhost:8080这样的地址,它不是https协议,因此没法设置securecookies,那么怎么办呢?

咱们只能动手改造咱们的本地服务器,使它也支持https协议:

第一步,咱们先生成根证书:

openssl genrsa -des3 -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

这时候咱们会获得两个文件,一个是rootCA.key,一个是rootCA.pem

咱们把pem文件导入系统的钥匙链,并给予它彻底的信任,这样之后再由它签发的证书才能被系统承认,不然即便咱们用它签发了证书,浏览器同样会拒绝。

第二步,生成localhost证书:

咱们先创建一个名为server.csr.cnf的文件:

[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=US
ST=RandomState
L=RandomCity
O=RandomOrganization
OU=RandomOrganizationUnit
emailAddress=hello@example.com
CN = localhost

而后执行如下命令生成server.key文件

openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <( cat server.csr.cnf )

再建立一个v3.ext文件:

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

而后执行如下命令生成server.crt文件:

openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext

好了,到此为止,这个server.keyserver.crt文件就是咱们真正须要的两个文件,咱们把它们引入系统,不一样的系统有不一样的引入方法,如下例子是react的方法,创建一个.env文件:

SSL_CRT_FILE=server.crt
SSL_KEY_FILE=server.key
HTTPS=true

这时候再启动你的工程,localhost就带有https协议了。


跨域就是这么麻烦,但不论如何麻烦,咱们不辞麻烦,也必定要解决它!

相关文章
相关标签/搜索