解決跨域問題的5種狀況

跨域主要涉及4個響應(yīng)頭:

  • Access-Control-Allow-Origin用于設(shè)置允許跨域請求源地址 (預(yù)檢請求和正式請求在跨域時候都會驗證)
  • Access-Control-Allow-Headers 跨域允許攜帶的特殊頭信息字段 (只在預(yù)檢請求驗證)
  • Access-Control-Allow-Methods 跨域允許的請求方法或者說HTTP動詞 (只在預(yù)檢請求驗證)
  • Access-Control-Allow-Credentials 是否允許跨域使用cookies,如果要跨域使用cookies姑子,可以添加上此請求響應(yīng)頭槽袄,值設(shè)為true(設(shè)置或者不設(shè)置床玻,都不會影響請求發(fā)送,只會影響在跨域時候是否要攜帶cookies西土,但是如果設(shè)置蔓搞,預(yù)檢請求和正式請求都需要設(shè)置)。

不過不建議跨域使用(項目中用到過福荸,不過不穩(wěn)定,有些瀏覽器帶不過去)肴掷,除非必要敬锐,因為有很多方案可以代替。

什么是預(yù)檢請求呆瞻?

當發(fā)生跨域條件時候台夺,覽器先詢問服務(wù)器,當前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單之中痴脾,以及可以使用哪些HTTP動詞和頭信息字段颤介。只有得到肯定答復(fù),瀏覽器才會發(fā)出正式的XMLHttpRequest請求,否則就報錯买窟。

動手模擬:

Nginx代理端口:22222 ,配置如下

server {
    listen       22222;
    server_name  localhost;
    location  / {
        proxy_pass  http://localhost:59200;
    }
}

測試代理是否成功丰泊,通過Nginx代理端口2222再次訪問接口薯定,能正常訪問

接下來開始用網(wǎng)站8080訪問Nginx代理后的接口地址始绍,報錯情況如下

情況1:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

通過錯誤信息可以很清晰的定位到錯誤(注意看標紅部分)priflight說明是個預(yù)請求,CORS 機制跨域會首先進行 preflight(一個 OPTIONS 請求)话侄, 該請求成功后才會發(fā)送真正的請求亏推。這一設(shè)計旨在確保服務(wù)器對 CORS 標準知情,以保護不支持 CORS 的舊服務(wù)器

通過錯誤信息年堆,我們可以得到是預(yù)檢請求的請求響應(yīng)頭缺少了 Access-Control-Allow-Origin吞杭。

錯哪里,我們改哪里就好了变丧。

修改Nginx配置信息如下:

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080';
       proxy_pass  http://localhost:59200;
    }
}

當滿懷歡喜的以為能解決后芽狗,發(fā)現(xiàn)還是報了同樣的問題

不過我們的配置沒什么問題,問題在Nginx

add_header 指令用于添加返回頭字段,當且僅當狀態(tài)碼為圖中列出的那些時有效痒蓬。如果想要每次響應(yīng)信息都攜帶頭字段信息童擎,需要在最后添加always(經(jīng)我測試,只有Access-Control-Allow-Origin這個頭信息需要加always攻晒,其他的不加always也會攜帶回來)顾复,那我們加上試試

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
       proxy_pass  http://localhost:59200;
    }
}

修改了配置后,發(fā)現(xiàn)生效了鲁捏,當然不是跨域就解決了芯砸,是上面這個問題已經(jīng)解決了,因為報錯內(nèi)容已經(jīng)變了给梅。

情況2:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

通過報錯信息提示可以得知假丧,是跨域瀏覽器默認行為的預(yù)請求(option請求)沒有收到ok狀態(tài)碼,此時再修改配置文件动羽,當請求為option請求時候包帚,給瀏覽器返回一個狀態(tài)碼(一般是204)

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
       if ($request_method = 'OPTIONS') {
            return 204;
       }
       proxy_pass  http://localhost:59200;
    }
}

當配置完后,發(fā)現(xiàn)報錯信息變了

情況3:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.

意思就是預(yù)請求響應(yīng)頭Access-Control-Allow-Headers中缺少頭信息authorization(各種情況會不一樣曹质,在發(fā)生跨域后婴噩,在自定義添加的頭信息是不允許的,需要添加到請求響應(yīng)頭Access-Control-Allow-Headers中羽德,以便瀏覽器知道此頭信息的攜帶是服務(wù)器承認合法的几莽,我這里攜帶的是authorization,其他的可能是token之類的宅静,缺什么加什么)章蚣,知道了問題所在,然后修改配置文件,添加對應(yīng)缺少的部分纤垂,再試試

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
       if ($request_method = 'OPTIONS') {
           add_header Access-Control-Allow-Headers 'authorization'; #為什么寫在if里面而不是接著Access-Control-Allow-Origin往下寫矾策?因為這里只有預(yù)檢請求才會檢查
           return 204;
      }
    proxy_pass http://localhost:59200;
}
}

此時發(fā)現(xiàn)報錯問題又回到了情況1

經(jīng)測試驗證,只要if ($request_method = 'OPTIONS') 里面寫了 add_header 峭沦,當為預(yù)檢請求時外部配置的都會失效贾虽,為什么?↓↓吼鱼。

官方文檔是這樣說的:

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

意思就是當前層級無 add_header 指令時蓬豁,則繼承上一層級的add_header。相反的若當前層級有了add_header菇肃,就應(yīng)該無法繼承上一層的add_header地粪。

配置修改如下:

server {
    listen       22222;
    server_name  localhost;
    location  / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'authorization';
            return 204;
        }
        proxy_pass  http://localhost:59200;
    }
}

此時改完發(fā)現(xiàn)跨域問題已經(jīng)解決了,

不過以上雖然解決了跨域問題琐谤,但是考慮后期可能Nginx版本更新,不知道這個規(guī)則會不會被修改蟆技,考慮到這樣的寫法可能會攜帶上兩個 Access-Control-Allow-Origin ,這種情況也是不允許的斗忌,下面會說到质礼。所以配置適當修改如下:

server {
    listen       22222;
    server_name  localhost;
    location  / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'authorization';
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        }
        proxy_pass  http://localhost:59200;
    }
}

情況4:

比較早期的API可能只用到了POST和GET請求,而Access-Control-Allow-Methods這個請求響應(yīng)頭跨域默認只支持POST和GET飞蹂,當出現(xiàn)其他請求類型時候几苍,同樣會出現(xiàn)跨域異常。

比如陈哑,我這里將請求的API接口請求方式從原來的GET改成PUT妻坝,在發(fā)起一次試試。在控制臺上會拋出錯誤:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

報錯內(nèi)容也講的很清楚惊窖,在這個預(yù)請求中刽宪,PUT方法是不允許在跨域中使用的,我們需要改下Access-Control-Allow-Methods的配置(缺什么加上么界酒,這里我只加了PUT圣拄,可以自己加全一點),讓瀏覽器知道服務(wù)端是允許的

server {
    listen 22222;
    server_name localhost;
    location / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'content-type,authorization';
            add_header Access-Control-Allow-Methods 'PUT';#為這么只加在這個if中毁欣,不再下面的if也加上庇谆?因為這里只有預(yù)檢請求會校驗,當然你加上也沒事凭疮。
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        }
        proxy_pass http://localhost:59200;
    }
}

這里注意一下饭耳,改成PUT類型后,Access-Control-Allow-Headers請求響應(yīng)頭又會自動校驗content-type這個請求頭执解,和情況3是一樣的寞肖,缺啥補啥就行了。如果不加上content-type,則會報如下錯誤新蟆。(想簡單的話觅赊,Access-Control-Allow-Headers和Access-Control-Allow-Methods可以設(shè)置為 * ,表示全都匹配。但是Access-Control-Allow-Origin就不建議設(shè)置成 * 了琼稻,為了安全考慮吮螺,限制域名是很有必要的。)

都加上后欣簇,問題就解決了规脸,這里報405是我服務(wù)端這個接口只開放了GET坯约,沒有開放PUT熊咽,而此刻我將此接口用PUT方法去請求,所以接口會返回這個狀態(tài)碼闹丐。

情況5:

最后再說一種情況横殴,就是后端處理了跨域,就不需要自己在處理了(這里吐槽下卿拴,某些后端工程師自己改服務(wù)端代碼解決跨域衫仑,但是又不理解其中原理,網(wǎng)上隨便找段代碼黏貼堕花,導(dǎo)致響應(yīng)信息可能處理不完全文狱,如method沒添加全,headers沒加到點上缘挽,自己用的那個可能復(fù)制過來的并不包含實際項目所用到的瞄崇,沒有添加options請求返回狀態(tài)碼等,導(dǎo)致Nginx再用通用的配置就會可能報以下異常)

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8080', but only one is allowed.

意思就是此刻Access-Control-Allow-Origin請求響應(yīng)頭返回了多個壕曼,而只允許有一個苏研,這種情況當然修改配置去掉Access-Control-Allow-Origin這個配置就可以了,不過遇到這種情況腮郊,建議Nginx配置和服務(wù)端自己解決跨域只選其一摹蘑。(這里注意如果按我上面的寫法,if $request_method = 'OPTIONS' 這個里面的Access-Control-Allow-Origin可不能刪除轧飞,刪除!='OPTIONS'里面的就好了衅鹿,因為這里如果是預(yù)檢請求直接就ruturn了,請求不會再轉(zhuǎn)發(fā)到59200服務(wù)过咬,如果也刪除了大渤,就會報和情況1一樣的錯誤。所以為什么說要不服務(wù)端代碼層面解決跨域援奢,要不就Nginx代理解決兼犯,不要混著搞,不然不明白原理的人,網(wǎng)上找一段代碼貼就很可能解決不了問題)

server {
    listen       22222;
    server_name  localhost;
    location  / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Allow-Methods '*';
            add_header Access-Control-Allow-Credentials 'true';
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
            add_header Access-Control-Allow-Credentials 'true';
        }
        proxy_pass  http://localhost:59200;
    }
}

或者:

server {
    listen       22222;
    server_name  localhost;
    location  / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        add_header Access-Control-Allow-Headers '*';
        add_header Access-Control-Allow-Methods '*';
        add_header Access-Control-Allow-Credentials 'true';
        if ($request_method = 'OPTIONS') {
            return 204;
        }
        proxy_pass  http://localhost:59200;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末切黔,一起剝皮案震驚了整個濱河市砸脊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纬霞,老刑警劉巖凌埂,帶你破解...
    沈念sama閱讀 211,496評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诗芜,居然都是意外死亡瞳抓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評論 3 385
  • 文/潘曉璐 我一進店門伏恐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孩哑,“玉大人,你說我怎么就攤上這事翠桦『嵫眩” “怎么了?”我有些...
    開封第一講書人閱讀 157,091評論 0 348
  • 文/不壞的土叔 我叫張陵销凑,是天一觀的道長丛晌。 經(jīng)常有香客問我,道長斗幼,這世上最難降的妖魔是什么澎蛛? 我笑而不...
    開封第一講書人閱讀 56,458評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮蜕窿,結(jié)果婚禮上谋逻,老公的妹妹穿的比我還像新娘。我一直安慰自己渠羞,他們只是感情好斤贰,可當我...
    茶點故事閱讀 65,542評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著次询,像睡著了一般荧恍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屯吊,一...
    開封第一講書人閱讀 49,802評論 1 290
  • 那天送巡,我揣著相機與錄音,去河邊找鬼盒卸。 笑死骗爆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蔽介。 我是一名探鬼主播摘投,決...
    沈念sama閱讀 38,945評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼煮寡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了犀呼?” 一聲冷哼從身側(cè)響起幸撕,我...
    開封第一講書人閱讀 37,709評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎外臂,沒想到半個月后坐儿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,158評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡宋光,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,502評論 2 327
  • 正文 我和宋清朗相戀三年貌矿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罪佳。...
    茶點故事閱讀 38,637評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡逛漫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菇民,到底是詐尸還是另有隱情尽楔,我是刑警寧澤,帶...
    沈念sama閱讀 34,300評論 4 329
  • 正文 年R本政府宣布第练,位于F島的核電站,受9級特大地震影響玛荞,放射性物質(zhì)發(fā)生泄漏娇掏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,911評論 3 313
  • 文/蒙蒙 一勋眯、第九天 我趴在偏房一處隱蔽的房頂上張望婴梧。 院中可真熱鬧,春花似錦客蹋、人聲如沸塞蹭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,744評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽番电。三九已至,卻和暖如春辆琅,著一層夾襖步出監(jiān)牢的瞬間漱办,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,982評論 1 266
  • 我被黑心中介騙來泰國打工婉烟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娩井,地道東北人。 一個月前我還...
    沈念sama閱讀 46,344評論 2 360
  • 正文 我出身青樓似袁,卻偏偏與公主長得像洞辣,于是被迫代替她去往敵國和親咐刨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,500評論 2 348

推薦閱讀更多精彩內(nèi)容