跨域,指的是瀏覽器不能執行其餘網站的腳本。 它是由瀏覽器的同源策略形成的,是瀏覽器施加的安全限制。 所謂同源策略是指,協議、端口、域名都相同。javascript
瀏覽器的同源策略會致使跨域,這裏同源策略又分爲如下兩種:html
瀏覽器執行腳本時,會檢查腳本是否同源,若是不是,則不會被執行。java
兩個域名不能跨過域名來發送數據和請求數據,不然就是不安全的,這種不安全也就是CSRF
(跨站請求僞造)攻擊。jquery
若是沒有AJAX同源策略,至關危險,咱們發起的每一次HTTP請求都會帶上請求地址對應的cookie,CSRF
攻擊是利用用戶的登陸態發起惡意請求。web
JSONP的產生:ajax
AJAX直接請求普通文件存在跨域無權限訪問的問題,不論是靜態頁面也好.json
不過咱們在調用js文件的時候又不受跨域影響,好比引入jquery框架的,或者是調用相片的時候api
凡是擁有src這個屬性的標籤均可以跨域例如<script>
<img>
<iframe>
跨域
若是想經過純web端跨域訪問數據只有一種可能,那就是把遠程服務器上的數據裝進js格式的文件裏.瀏覽器
而json又是一個輕量級的數據格式,還被js原生支持
爲了便於客戶端使用數據,逐漸造成了一種非正式傳輸協議,人們把它稱做JSONP,該協議的一個要點就是容許用戶傳遞一個callback 參數給服務端。
基於script
跨域
<script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
複製代碼
在開發中可能會遇到多個 JSONP 請求的回調函數名是相同的,這時候就須要本身封裝一個 JSONP,如下是簡單實現:
function jsonp(url, jsonpCallback, success) {
let script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
console.log(value)
})
複製代碼
基於jQuery
跨域
$.ajax({
url:'http://domain/api',
ype : 'get',
dataType : 'text',
success:function(data){
alert(data);
},
error:function(data){
alert(2);
}
});
複製代碼
經過iframe
來跨子域
eg: a.study.cn/a.html 請求 b.study.cn/b.html
a.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
function test() {
alert(document.getElementById('a').contentWindow);
}
</script>
</head>
<body>
<iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
</html>
複製代碼
b.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
</script>
</head>
<body>
我是b.study.cn的body
</body>
</html>
複製代碼
修改document.domain的方法只適用於不一樣子域的框架(父類與子類)間的交互。
支持CORS請求的瀏覽器一旦發現ajax請求跨域,會對請求作一些特殊處理,對於已經實現CORS接口的服務端,接受請求,並作出迴應。
有一種狀況比較特殊,若是咱們發送的跨域請求爲「非簡單請求」,瀏覽器會在發出此請求以前首先發送一個請求類型爲OPTIONS的「預檢請求」,驗證請求源是否爲服務端容許源,這些對於開發這來講是感受不到的,由瀏覽器代理。
總而言之,客戶端不須要對跨域請求作任何特殊處理。
瀏覽器對跨域請求區分爲「簡單請求」與「非簡單請求」
簡單請求
(1) 請求方法是如下三種方法之一:
HEAD
GET
POST
複製代碼
(2)HTTP的頭信息不超出如下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:
application/x-www-form-urlencoded、 multipart/form-data、text/plain
複製代碼
不知足這些特徵的請求稱爲「非簡單請求」,例如:content-type=applicaiton/json , method = PUT/DELETE...
瀏覽器判斷跨域爲簡單請求時候,會在Request Header中添加 Origin (協議 + 域名 + 端口)字段 , 它表示咱們的請求源,CORS服務端會將該字段做爲跨源標誌。
必須字段:
Access-Control-Allow-Origin:表示服務端容許的請求源,*標識任何外域,多個源 , 分隔
可選字段
Access-Control-Allow-Credentials:false 表示是否容許發送Cookie,設置爲true
同時,ajax請求設置withCredentials = true,瀏覽
器的cookie就能發送到服務端
Access-Control-Expose-Headers:調用getResponseHeader()方法時候,能從header中獲
取的參數
複製代碼
總結:簡單請求只須要CORS服務端在接受到攜帶Origin字段的跨域請求後,在response header中添加Access-Control-Allow-Origin等字段給瀏覽器作同源判斷
進行非簡單請求時候, 瀏覽器會首先發出類型爲OPTIONS的「預檢請求」,請求地址相同 , CORS服務端對「預檢請求」處理,並對Response Header添加驗證字段,客戶端接受到預檢請求的返回值進行一次請求預判斷,驗證經過後,主請求發起。
例如:發起 content-type=application/json 的非簡單請求,這時候傳參要注意爲json字符串
這裏能夠看到,瀏覽器連續發送了兩個jsonp.do請求 , 第一個就是「預檢請求」,類型爲OPTIONS,由於咱們設置了content-type這個屬性,因此預檢請求的Access-Control-Expose-Headers必須攜帶content-type,不然預檢會失敗。
預檢經過後,主請求與簡單請求一致。
總結:非簡單請求須要CORS服務端對OPTIONS類型的請求作處理,其餘與簡單請求一致
經過上面敘述,咱們得知藉助CORS咱們沒必要關心發出的請求是否跨域,瀏覽器會幫咱們處理這些事情,可是服務端須要支持CORS,服務端實現CORS的原理也很簡單,在服務端徹底能夠對請求作上下文處理,已達到接口容許跨域訪問的目的。
瀏覽器有跨域限制,可是服務器不存在跨域問題,因此能夠由服務器請求所要域的資源再返回給客戶端。
該方式只能用於二級域名相同的狀況下,好比 a.test.com 和 b.test.com 適用於該方式。
只須要給頁面添加 document.domain = 'test.com' 表示二級域名都相同就能夠實現跨域。
location.hash方式跨域,是子框架具備修改父框架src的hash值,經過這個屬性進行傳遞數據,且更改hash值,頁面不會刷新。可是傳遞的數據的字節數是有限的。
頁面和新開的窗口的數據交互。
這個應該就是之後解決dom跨域通用方法了,具體能夠參照MDN。
// 發送消息端
window.parent.postMessage('message', 'http://test.com')
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {
var origin = event.origin || event.originalEvent.origin
if (origin === 'http://test.com') {
console.log('驗證經過')
}
})
複製代碼
參考: