跨域这个话题曾经谈了很多年了,怎么当初又要谈这个问题?原本是能够不用再提了的,然而因为 Chrome 86
版本当前又减少了很多限度,导致咱们不得不再次提起。
CORS
对于前端开发来说,跨域通常有两种形式,一种是在服务端批改 nginx
配置,在 response headers
里增加 CORS
设置,另一种是在本地架设代理。咱们先谈第一种。
本来在 nginx
里增加 CORS
曾经是一种惯例操作,简略到变本加厉:
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
随申请一并发送了。
但问题在 Chrome 86
版本当前,Chrome
强制给从服务端下发的每个 cookie
都减少了一个缺省的 SameSite
属性,并且强制把这个属性设置为 Lax
,从而导致咱们的withCredentials
不起作用,只有是跨域的状况,cookies
连取都取不到,更不要提发送了。在这里我不想再详述 SameSite
的原理,感兴趣的能够去这里理解。
这就须要咱们必须从服务端动手,批改 nginx
下发 cookies
时的 SameSite
属性。
SameSite 和 secure
批改 SameSite
又分为两种状况:如果你用的是低版本的 nginx
,可能还不肯定能齐全解决问题,目前惟一能批改的变通之道是批改cookie
的path
,使它前面带上 SameSite
属性,例如这样:
proxy_cookie_path ~(.*) "$1; SameSite=none";
这里理论批改的是 cookie
里的 path
值,但因为附加了 ;
,所以浏览器会认为自;
当前的是新属性,所以也会承受这个 SameSite
设定。
然而仅此还不够,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
协定,所以无奈设置 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
协定了。
跨域就是这么麻烦,但不论如何麻烦,咱们不辞麻烦,也肯定要解决它!