跨域这个话题曾经谈了很多年了,怎么当初又要谈这个问题?原本是能够不用再提了的,然而因为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 2048openssl 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 = 2048prompt = nodefault_md = sha256distinguished_name = dn[dn]C=USST=RandomStateL=RandomCityO=RandomOrganizationOU=RandomOrganizationUnitemailAddress=hello@example.comCN = 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,issuerbasicConstraints=CA:FALSEkeyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEnciphermentsubjectAltName = @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.crtSSL_KEY_FILE=server.keyHTTPS=true
这时候再启动你的工程,localhost
就带有https
协定了。
跨域就是这么麻烦,但不论如何麻烦,咱们不辞麻烦,也肯定要解决它!