跨域这个话题已经谈了不少年了,怎么如今又要谈这个问题?原本是能够没必要再提了的,可是因为Chrome 86
版本之后又增长了不少限制,致使咱们不得再也不次提起。html
对于前端开发来讲,跨域一般有两种方式,一种是在服务端修改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
又分为两种状况:若是你用的是低版本的nginx
,可能还不必定能彻底解决问题,目前惟一能修改的变通之道是修改cookie
的path
,使它后面带上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
标志的方法相似,参考官方文档,不赘述。
由上可知,咱们能够经过修改nginx
的方法来改造跨域cookies
的发送。但这时若是你又不想跨域了,还想用本地代理的方式来解决,又会遇到一个问题:由于咱们上面在下发cookies
的时候,缺省地给每一个cookie
都带上了secure
属性,这就致使咱们在使用本地代理的时候反而没法设置cookies
,由于咱们本地的开发服务器每每使用的是相似于http://localhost:8080
这样的地址,它不是https
协议,因此没法设置secure
的cookies
,那么怎么办呢?
咱们只能动手改造咱们的本地服务器,使它也支持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.key
和server.crt
文件就是咱们真正须要的两个文件,咱们把它们引入系统,不一样的系统有不一样的引入方法,如下例子是react
的方法,创建一个.env
文件:
SSL_CRT_FILE=server.crt SSL_KEY_FILE=server.key HTTPS=true
这时候再启动你的工程,localhost
就带有https
协议了。
跨域就是这么麻烦,但不论如何麻烦,咱们不辞麻烦,也必定要解决它!