什么是跨域問題薛闪?
為什么會出現(xiàn)跨域問題?
因為瀏覽器的同源策略
(同源指的是:協(xié)議+域名+端口
相同)。
同源策略
是瀏覽器的安全策略忠蝗,限制某一個域的網(wǎng)站或者網(wǎng)站加載的腳本如何與另一個域的資源進行交互。
也就是瀏覽器
為了保護當(dāng)前的網(wǎng)站用戶的信息安全漓拾,限制了當(dāng)前網(wǎng)站與其他域的服務(wù)的交互阁最。所謂的限制并非阻止,而是通過一些約定好的方式骇两,決定是否進行這次跨域交互速种。
為什么瀏覽器要有同源策略
沒有同源策略限制的接口請求
先補充一個context:存儲在瀏覽器上的cookie會在瀏覽器向同一服務(wù)器再次發(fā)起請求時被自動攜帶,并發(fā)往server低千。
如果沒有同源策略的限制配阵,在下面這種csrf攻擊的情況下,你將會受到很大的影響
- 你使用用戶名和密碼登錄了
www.yinhang.com
網(wǎng)站示血,并且獲取到了cookic并存放在了本地棋傍。 - 之后給這個銀行server發(fā)送的
www.yinhang.com/getMoney
等請求都會自動的攜帶這個cookie作為用戶憑證給server發(fā)送請求 - 突然你某一個地方誘導(dǎo)你點擊了一個壞網(wǎng)站
www.yinghangbad.com
,這時候你點進去难审,這個網(wǎng)站偷偷的給銀行的server發(fā)送了請求www.yinhang.com/getMoney
獲取你的賬戶信息瘫拣,顯而易見,這個請求和網(wǎng)站的域名并不相同告喊,如果沒有同源策略的限制麸拄,這個請求會被輕松的攜帶你的cookie從銀行server成功的獲取money信息
沒有同源策略限制的Dom查詢
某一個壞的網(wǎng)站利用<iframe>將銀行網(wǎng)站嵌入到自己的網(wǎng)站中,想要通過自己的寫的js文件黔姜,拿到嵌入的銀行網(wǎng)站的HTML username和password所在的input框的value拢切。
如果沒有瀏覽器的同源策略:只能access同源網(wǎng)站上的DOM元素
, 那么壞網(wǎng)站上的JS就可以access銀行網(wǎng)站上的DOM元素秆吵,這將會造成用戶名和密碼泄露淮椰。
由此可見瀏覽器的同源策略確實能在某種程度上保護了你的網(wǎng)站。
同源策略導(dǎo)致了:
-
非同源的請求無法通信,真實流程是:
- 瀏覽器發(fā)現(xiàn)當(dāng)前的網(wǎng)站正在給一個非同源的服務(wù)發(fā)送請求主穗,瀏覽器在請求的header中加上一個
Origin
標識來自的源泻拦,幫助service判斷是否需要相應(yīng)來自這個域的請求 - 當(dāng)請求的response回到瀏覽器,瀏覽器會首先檢查header中是否帶有
Access-Control-Allow-Origin
, 并且檢查該字段的value中是否包含當(dāng)前網(wǎng)站所在的域(也就是由server決定是否允許這個域的請求)黔牵,只有包含聪轿,瀏覽器才會將請求的response返回給網(wǎng)站。
- 瀏覽器發(fā)現(xiàn)當(dāng)前的網(wǎng)站正在給一個非同源的服務(wù)發(fā)送請求主穗,瀏覽器在請求的header中加上一個
非同源的DOM或者JS無法訪問
非同源的cookie猾浦、LocalStorage陆错、IndexDB無法讀寫
但是DOM的同源策略可以理解,對于網(wǎng)站上的所有請求總不能因為不同源而被全部阻斷金赦,那么如何允許跨域的請求呢音瓷?
會受到同源策略影響的資源
以下是會加載外部資源的HTML標簽:
- script標簽對JS文件的請求
- link標簽對css文件的請求
- Img對image的請求
- 通過 @font-face 引入的字體。一些瀏覽器允許跨域字體( cross-origin fonts)夹抗,一些需要同源字體(same-origin fonts)绳慎。
- 通過 <video> 和 <audio> 播放的多媒體資源。
- 通過 <object>漠烧、 <embed> 和 <applet> 嵌入的插件杏愤。
- 通過 <iframe> 載入的任何資源。站點可以使用 X-Frame-Options 消息頭來阻止這種形式的跨域交互已脓。
會受到同源策略影響的資源
- font
- 所有ajax請求
在瀏覽器中, <script> 珊楼、<img>、<iframe>度液、<link>等標簽都可以跨域加載,而不受瀏覽器的同源策略的限制
如何解決因同源策略帶來的跨域請求失敗
一般我們都是采用CORS(跨域資源共享)解決此類問題的
跨域資源共享是一種基于HTTP header
的機制厕宗。這個機制的原理:
某一個ajax請求從一個網(wǎng)站發(fā)出,請求的server是一個和當(dāng)前網(wǎng)站域名不一致的server堕担,也就是說這是一個跨域請求已慢。
請求剛開始被fire的時候,經(jīng)過瀏覽器霹购,瀏覽器檢測到這是一個跨域請求佑惠,就會自動在這個request的header中加上:
Origin
字段,value就是當(dāng)前網(wǎng)站protocol://host:port
比如https://developer.mozilla.org
這個字段是不能夠被修改的請求到達Server之后厕鹃, 服務(wù)器可以檢查這個origin兢仰,并決定是否給這個origin提供服務(wù),提供怎樣的服務(wù)剂碴。
如果服務(wù)器最終決定給這個請求提供服務(wù),那么服務(wù)器就需要讓瀏覽器知道轻专,因此需要在response header中帶上
Access-Control-Allow-Origin
value中就是服務(wù)器能夠服務(wù)的域名
Access-Control-Allow-Origin: <origin> | *
注意這里的origin是能是一個域名不能是多個
- 瀏覽器接受到來自response忆矛,會首先check header中是否包含
Access-Control-Allow-Origin
,并查看當(dāng)前的域是否在value中。如果是催训,瀏覽器會把response成功返回給網(wǎng)站洽议,如果不是,那么就會報錯漫拭。
ps: 當(dāng)然還包含很多其他的header亚兄,一般Access-Controll-XXX-XXX
都是和跨域相關(guān)的header
當(dāng)然以上只是CORS的一種場景,正常情況根據(jù)請求的不同采驻,跨域機制也不同:
簡單請求
通常請求類型是:
- GET
- HEAD
- POST
并且header中沒有特殊的內(nèi)容审胚,只有常見的accept、content-type等
且content-type只能是application/x-www-form-urlencoded
text/plain
符合上述條件的請求一般就是簡單請求礼旅,簡單請求的跨域機制就符合上面的步驟膳叨,只要有origin
以及access-controllXX
注意如果請求的header中content-type是application/json
這類請求一定不是簡單請求。
非簡單請求(復(fù)雜請求)
較為復(fù)雜的不在上面簡單請求類型的請求就是復(fù)雜請求
痘系,瀏覽器在發(fā)出這類請求之前會發(fā)出一個預(yù)檢請求
預(yù)檢請求
是當(dāng)你要發(fā)出一個跨域請求時菲嘴,瀏覽器會自動率先發(fā)出一個OPTION類型的請求,帶上Origin
, 詢問服務(wù)器是否能夠允許接下來的實際復(fù)雜請求汰翠,預(yù)檢請求會在請求頭部帶上實際請求的相關(guān)信息:
Access-Control-Request-Method: POST
// 實際請求的method
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
// 實際請求中即將有的header
服務(wù)器會根據(jù)預(yù)檢請求帶來的實際請求的信息龄坪,決定是否允許接下來的實際請求,如果允許复唤,會在response的header中攜帶
Access-Control-Allow-Methods: GET, POST, PUT
// 必須字段健田,標明當(dāng)前服務(wù)器可以允許的來自此域的所有請求method
Access-Control-Allow-Headers: X-Custom-Header
// 標明當(dāng)前服務(wù)器可以允許的來自此域的所有請求能攜帶的header字段
Access-Control-Allow-Credentials: true
// 后續(xù)會提到
Access-Control-Max-Age: 1728000
// 可選字段,標明本次預(yù)檢請求的有效時間苟穆,在有效時間內(nèi)抄课,瀏覽器無須為同一請求再次發(fā)起預(yù)檢請求。請注意雳旅,瀏覽器自身維護了一個最大有效時間跟磨,如果該首部字段的值超過了最大有效時間,將不會生效攒盈。
瀏覽器會根據(jù)預(yù)檢請求的response決定是否發(fā)送實際請求給服務(wù)器抵拘。
之后的實際請求會按照上面簡單請求的流程,request會被瀏覽器攜帶origin
, response必須有Access-Controll-Allow-XXX
帶credentials的請求
通常HTTP請求可以通過cookie
或者 Authorization header
給服務(wù)器發(fā)送身份憑證型豁。
但是對于跨域的請求僵蛛,在沒有特殊設(shè)置的前提下,瀏覽器不會發(fā)送身份憑證信息給服務(wù)器迎变,也就是說任何cookie或者authorization都不會自動的攜帶到某一個請求上充尉。這是瀏覽器的同源策略之一。
那么如何將身份信息攜帶在請求中呢衣形?
需要雙重設(shè)置:
需要在XMLHttpRequest或者fetch上設(shè)置
request.withCredentials = true
驼侠, 瀏覽器才會在請求上攜帶身份信息服務(wù)器接收到身份信息處理完畢之后姿鸿,必須在response中攜帶
Access-Control-Allow-Credentials: true
否則瀏覽器不會把響應(yīng)內(nèi)容返回給請求的發(fā)送者。對于這種攜帶身份信息的請求倒源,它response的
Access-Control-Allow-Origin
的value不能是*
苛预,只能是origin