RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Nginx輕松搞定跨域問題

jf_ro2CN3Fa ? 來源:芋道源碼 ? 2023-06-11 11:39 ? 次閱讀


當(dāng)你遇到跨域問題,不要立刻就選擇復(fù)制去嘗試,請詳細看完這篇文章再處理,我相信它能幫到你。

分析前準備:

前端網(wǎng)站地址:http://localhost:8080

服務(wù)端網(wǎng)址:http://localhost:59200

首先保證服務(wù)端是沒有處理跨域的,其次,先用postman測試服務(wù)端接口是正常的

7b2d69d6-07fc-11ee-962d-dac502259ad0.png

當(dāng)網(wǎng)站8080去訪問服務(wù)端接口時,就產(chǎn)生了跨域問題,那么如何解決?接下來我把跨域遇到的各種情況都列舉出來并通過nginx代理的方式解決(后臺也是一樣的,只要你理解的原理)。

跨域主要涉及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)定,有些瀏覽器帶不過去),除非必要,因為有很多方案可以代替。

網(wǎng)上很多文章都是告訴你直接Nginx添加這幾個響應(yīng)頭信息就能解決跨域,當(dāng)然大部分情況是能解決,但是我相信還是有很多情況,明明配置上了,也同樣會報跨域問題。

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

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

7b56e0b8-07fc-11ee-962d-dac502259ad0.png

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

開始動手模擬:

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

server{
listen22222;
server_namelocalhost;
location/{
proxy_passhttp://localhost:59200;
}
}

測試代理是否成功,通過Nginx代理端口2222再次訪問接口,可以看到如下圖通過代理后接口也是能正常訪問

7b81b2a2-07fc-11ee-962d-dac502259ad0.png

接下來開始用網(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.

7ba44c86-07fc-11ee-962d-dac502259ad0.png

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

通過錯誤信息,我們可以得到是預(yù)檢請求的請求響應(yīng)頭缺少了 Access-Control-Allow-Origin,錯哪里,我們改哪里就好了。修改Nginx配置信息如下(紅色部分為添加部分),缺什么就補什么,很簡單明了

server{
listen22222;
server_namelocalhost;
location/{
add_headerAccess-Control-Allow-Origin'http://localhost:8080';
proxy_passhttp://localhost:59200;
}
}

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

7ba44c86-07fc-11ee-962d-dac502259ad0.png

不過我們的配置沒什么問題,問題在Nginx,下圖鏈接http://nginx.org/en/docs/http/ngx_http_headers_module.html

7bf35614-07fc-11ee-962d-dac502259ad0.png

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

server{
listen22222;
server_namelocalhost;
location/{
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
proxy_passhttp://localhost:59200;
}
}

修改了配置后,發(fā)現(xiàn)生效了,當(dāng)然不是跨域就解決了,是上面這個問題已經(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.

7c1acf3c-07fc-11ee-962d-dac502259ad0.png

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

server{
listen22222;
server_namelocalhost;
location/{
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
if($request_method='OPTIONS'){
return204;
}
proxy_passhttp://localhost:59200;
}
}

當(dāng)配置完后,發(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.

7c351edc-07fc-11ee-962d-dac502259ad0.png

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

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

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

7c5a61d8-07fc-11ee-962d-dac502259ad0.png

經(jīng)測試驗證,只要if ($request_method = 'OPTIONS') 里面寫了 add_header ,當(dāng)為預(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.

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

7c7c3dbc-07fc-11ee-962d-dac502259ad0.png

配置修改如下:

server{
listen22222;
server_namelocalhost;
location/{
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
if($request_method='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080';
add_headerAccess-Control-Allow-Headers'authorization';
return204;
}
proxy_passhttp://localhost:59200;
}
}

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

7ca8754e-07fc-11ee-962d-dac502259ad0.png

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

server{
listen22222;
server_namelocalhost;
location/{
if($request_method='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080';
add_headerAccess-Control-Allow-Headers'authorization';
return204;
}
if($request_method!='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
}
proxy_passhttp://localhost:59200;
}
}

還沒完,繼續(xù)聊 ↓↓

情況4:

比較早期的API可能只用到了POST和GET請求,而Access-Control-Allow-Methods這個請求響應(yīng)頭跨域默認只支持POST和GET,當(dāng)出現(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.

7ccbd4ee-07fc-11ee-962d-dac502259ad0.png

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

server{
listen22222;
server_namelocalhost;
location/{
if($request_method='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080';
add_headerAccess-Control-Allow-Headers'content-type,authorization';
add_headerAccess-Control-Allow-Methods'PUT';#為這么只加在這個if中,不再下面的if也加上?因為這里只有預(yù)檢請求會校驗,當(dāng)然你加上也沒事。
return204;
}
if($request_method!='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
}
proxy_passhttp://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è)置成 * 了,為了安全考慮,限制域名是很有必要的。)

7cf54f86-07fc-11ee-962d-dac502259ad0.png

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

7d191e98-07fc-11ee-962d-dac502259ad0.png

情況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.

7d504030-07fc-11ee-962d-dac502259ad0.png7d741906-07fc-11ee-962d-dac502259ad0.png

意思就是此刻Access-Control-Allow-Origin請求響應(yīng)頭返回了多個,而只允許有一個,這種情況當(dā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)上找一段代碼貼就很可能解決不了問題)

↓ ↓ ↓ ↓ ↓

再貼一份完整配置(*號根據(jù)自己‘喜好’填寫):

server{
listen22222;
server_namelocalhost;
location/{
if($request_method='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080';
add_headerAccess-Control-Allow-Headers'*';
add_headerAccess-Control-Allow-Methods'*';
add_headerAccess-Control-Allow-Credentials'true';
return204;
}
if($request_method!='OPTIONS'){
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
add_headerAccess-Control-Allow-Credentials'true';
}
proxy_passhttp://localhost:59200;
}
}

或者:

server{
listen22222;
server_namelocalhost;
location/{
add_headerAccess-Control-Allow-Origin'http://localhost:8080'always;
add_headerAccess-Control-Allow-Headers'*';
add_headerAccess-Control-Allow-Methods'*';
add_headerAccess-Control-Allow-Credentials'true';
if($request_method='OPTIONS'){
return204;
}
proxy_passhttp://localhost:59200;
}
}

最后,這是一篇解決跨域遇到問題解決問題的過程,如果認真看完了,我相信應(yīng)該都能很容易的理解,并且在實際使用中自己解決該問題,希望能幫助到大家,以上內(nèi)容都是自己理解自己測試碼出來的,如有理解不對的地方,望大家指正。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • HTTP
    +關(guān)注

    關(guān)注

    0

    文章

    504

    瀏覽量

    31194
  • 服務(wù)端
    +關(guān)注

    關(guān)注

    0

    文章

    66

    瀏覽量

    7004
  • nginx
    +關(guān)注

    關(guān)注

    0

    文章

    149

    瀏覽量

    12170

原文標題:Nginx 輕松搞定跨域問題

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    采用Nginx的反向代理解決

    40Nginx的反向代理功能解決問題
    發(fā)表于 10-10 10:58

    ajax如何克服

    如何克服ajax
    發(fā)表于 04-30 13:25

    如何處理好FPGA設(shè)計中時鐘間的數(shù)據(jù)

    時鐘處理是FPGA設(shè)計中經(jīng)常遇到的問題,而如何處理好時鐘間的數(shù)據(jù),可以說是每個FPGA初學(xué)者的必修課。如果是還是在校的學(xué)生,時鐘
    發(fā)表于 07-29 06:19

    如何處理好時鐘間的數(shù)據(jù)呢

    時鐘處理是什么意思?如何處理好時鐘間的數(shù)據(jù)呢?有哪幾種時鐘處理的方法呢?
    發(fā)表于 11-01 07:44

    FPGA時鐘處理簡介

    (10)FPGA時鐘處理1.1 目錄1)目錄2)FPGA簡介3)Verilog HDL簡介4)FPGA時鐘處理5)結(jié)語1.2 FPGA簡介FPGA(Field Programm
    發(fā)表于 02-23 07:47

    看看Stream信號里是如何做時鐘握手的

    邏輯出身的農(nóng)民工兄弟在面試時總難以避免“時鐘”的拷問,在諸多時鐘的方法里,握手是一種常見的方式,而Stream作為一種天然的握手信號,不妨看看它里面是如做
    發(fā)表于 07-07 17:25

    cdc路徑方案幫您解決時鐘難題

    這一章介紹一下CDC也就是時鐘可能存在的一些問題以及基本的時鐘處理方法。時鐘的問題主
    的頭像 發(fā)表于 11-30 06:29 ?7184次閱讀
    cdc路徑方案幫您解決<b class='flag-5'>跨</b>時鐘<b class='flag-5'>域</b>難題

    關(guān)于時鐘的詳細解答

    每一個做數(shù)字邏輯的都繞不開時鐘處理,談一談SpinalHDL里用于時鐘處理的一些手段方法。
    的頭像 發(fā)表于 04-27 10:52 ?4266次閱讀
    關(guān)于<b class='flag-5'>跨</b>時鐘<b class='flag-5'>域</b>的詳細解答

    輕松搞定立磨搖臂軸承室磨損問題

    輕松搞定立磨搖臂軸承室磨損問題
    發(fā)表于 03-28 15:34 ?1次下載

    時鐘電路設(shè)計總結(jié)

    時鐘操作包括同步時鐘操作和異步時鐘操作。
    的頭像 發(fā)表于 05-18 09:18 ?735次閱讀
    <b class='flag-5'>跨</b>時鐘<b class='flag-5'>域</b>電路設(shè)計總結(jié)

    FPGA時鐘處理方法(一)

    時鐘是FPGA設(shè)計中最容易出錯的設(shè)計模塊,而且一旦時鐘出現(xiàn)問題,定位排查會非常困難,因為時鐘
    的頭像 發(fā)表于 05-25 15:06 ?1985次閱讀
    FPGA<b class='flag-5'>跨</b>時鐘<b class='flag-5'>域</b>處理方法(一)

    FPGA時鐘處理方法(二)

    上一篇文章已經(jīng)講過了單bit時鐘的處理方法,這次解說一下多bit的時鐘方法。
    的頭像 發(fā)表于 05-25 15:07 ?1021次閱讀
    FPGA<b class='flag-5'>跨</b>時鐘<b class='flag-5'>域</b>處理方法(二)

    基于Nginx配置origin限制請求

    按照等保要求,的不安全性,需要修復(fù)。 這個需要根據(jù)客戶端傳遞的請求頭中的Origin值,進行安全的站策略配置,目的是對非法的origin直接返回403錯誤頁面。 漏洞復(fù)現(xiàn) 復(fù)現(xiàn)方式為在
    的頭像 發(fā)表于 06-26 10:49 ?1348次閱讀
    基于<b class='flag-5'>Nginx</b>配置origin限制<b class='flag-5'>跨</b><b class='flag-5'>域</b>請求

    如何解決問題

    如何解決問題?首先我們需要知道什么是,指的是瀏覽器不能執(zhí)行其它網(wǎng)站的腳本,它是由瀏覽
    的頭像 發(fā)表于 10-09 16:07 ?554次閱讀
    如何解決<b class='flag-5'>跨</b><b class='flag-5'>域</b>問題

    學(xué)會這4招,輕松搞定開關(guān)電源EMI

    學(xué)會這4招,輕松搞定開關(guān)電源EMI
    的頭像 發(fā)表于 12-05 16:57 ?835次閱讀
    學(xué)會這4招,<b class='flag-5'>輕松</b><b class='flag-5'>搞定</b>開關(guān)電源EMI
    RM新时代网站-首页