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協議了。


跨域就是這麼麻煩,但不論如何麻煩,咱們不辭麻煩,也必定要解決它!

相關文章
相關標籤/搜索