JavaScript出於安全方面的考慮作的同源策略的限制,不容許跨域訪問其餘資源。一般跨域請求成功後,瀏覽器會拒絕響應服務器端返回的結果。javascript
同源政策的目的是爲了防止惡意網站竊取用戶數據信息冒充用戶作一些操做。同源限制只是提升攻擊成本。若是沒有JavaScript同源限制:html
域名、協議、端口均相同。舉例來講,http://www.example.com/dir/page.html
這個網址,協議是http://
,域名是www.example.com
,端口是80
(默認端口能夠省略)java
<img src="URL"> <link href="URL"> <script src="URL"> <iframe src="URL"> <form action="URL" method="get/post"> First name: <input type="text" name="fname"><br> Last name: <input type="text" name="lname"><br> <input type="submit" value="提交"> </form>
另外,若是是非同源的網頁,目前容許經過 JavaScript 腳本能夠拿到其餘窗口/網頁的window
對象的九個屬性和四個方法。web
其中,只有window.location
是可讀寫(非同源的狀況下,也只容許調用location.replace
方法和寫入location.href
屬性)的,其餘八個所有都是隻讀。json
以下相對http://store.company.com/dir/page.html
同源檢測的示例:api
URL | 結果 | 緣由 |
---|---|---|
http://store.company.com/dir2/other.html |
成功 | 只有路徑不一樣 |
http://store.company.com/dir/inner/another.html |
成功 | 只有路徑不一樣 |
https://store.company.com/secure.html |
失敗 | 不一樣協議 ( https和http ) |
http://store.company.com:81/dir/etc.html |
失敗 | 不一樣端口 ( http:// 80是默認的) |
http://news.company.com/dir/other.html |
失敗 | 不一樣域名 ( news和store ) |
注意:域名與其對應的ip也不能成功訪問跨域
document.domain
共享 Cookie,拿到 DOM。/****A網頁:http://t1.example.com/a.html*/ document.domain = 'example.com'; //設置cookie document.cookie = "test1=hello"; /****B網頁:http://t2.example.com/b.html,設置相同的document.domain*/ document.domain = 'example.com'; //訪問A網頁的cookie console.log(document.cookie); /*注意:A 和 B 兩個網頁都須要設置document.domain屬性,才能達到同源的目的。由於設置document.domain的同時,會把端口重置爲null,所以若是隻設置一個網頁的document.domain,會致使兩個網址的端口不一樣,仍是達不到同源的目的*/
Set-Cookie: key=value; domain=.example.com; path=/
缺點:這種方法只適用於共享 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 沒法經過這種方法跨域共享瀏覽器
#
號後面的部分)只是改變url的片斷標識符,頁面不會從新刷新,父窗口能夠把信息,寫入子窗口的片斷標識符。緩存
//****父窗口把要共享的信息添加到子窗口url的#後 var src = originURL + '#' + data; document.getElementById('myIFrame').src = src; //****子窗口監聽窗口變化 window.onhashchange = ()=>{ //獲取url的#後的數據 var data= window.location.hash; } //子窗口也能夠經過這種方式向父窗口共享信息 parent.location.href= target + "#" + hash;
瀏覽器窗口的window.name
屬性,只要在同一個窗口裏不管是否同源,前一個網頁設置了這個屬性,後一個網頁能夠讀取它,且容量很大,能夠放置很是長的字符串。安全
//****子窗口:http://child.url.com/xxx.html,將信息寫入window.name屬性: window.name = data; location = 'http://parent.url.com/other.html';//接着,子窗口跳回一個與父窗口同域的網址。 //****父窗口:http://parent.url.com/xxx.html,先打開不一樣源的子窗口網頁: var iframe = document.createElement('iframe'); iframe.id='myFrame'; iframe.src = 'http://child.url.com/xxx.html';//iframe能夠跨域加載資源 document.body.appendChild(iframe); //而後,父窗口就能夠讀取子窗口的window.name了。 var data = document.getElementById('myFrame').contentWindow.name;
缺點:必須監聽子窗口window.name
屬性的變化,影響網頁性能。
HTML5爲了解決Window對象之間的跨源通訊問題(例如:在頁面和它的彈出窗口之間,或嵌入其中的iframe之間,具體參見:https://www.w3cschool.cn/fetch_api/fetch_api-lx142x8t.html),引入了——跨文檔通訊 API。這個API爲window
對象新增了一個window.postMessage
方法,容許跨窗口通訊,不論這兩個窗口是否同源。
/*語法:otherWindow.postMessage(message, targetOrigin, [transfer]); message:要發送的數據信息 targetOrigin:接收消息的窗口的源(origin),即"協議 + 域名 + 端口"。也能夠設爲*,表示不限制域名,向全部窗口發送。 */ //****父窗口"http://aaa.com"向子窗口"http://bbb.com"發消息 var popup = window.open('http://bbb.com', 'title'); popup.postMessage('Hello World!', 'http://bbb.com'); //****子窗口經過message事件,監聽發送者的消息 window.addEventListener('message', function(event) { console.log(event.source);//發送源自的窗口:popup (子窗口能夠經過event.source屬性引用父窗口,而後發送消息) console.log(event.origin);//發送源自的域:"http://aaa.com"(經過event.origin驗證發送者,分派事件的origin屬性的值不受調用窗口中document.domain的當前值的影響) console.log(event.data);//消息內容:'Hello World!' },false);
安全問題
①若是您不但願從其餘網站接收message,請不要爲message事件添加任何事件偵聽器,這是一個徹底萬無一失的方式來避免安全問題
②若是您確實但願從其餘網站接收message,請始終使用origin和source屬性驗證發件人的身份,以避免收到惡意網站發送的惡意信息。
③當您使用postMessage將數據發送到其餘窗口時,始終指定精確的目標origin,而不是*,以避免被惡意網站中間攔截postMessage發送的信息。
詳情查看:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
同源政策規定,AJAX請求只能發給同源的網址,不然就報錯。三種解決方案:
瀏覽器請求同源服務器,再由後者請求外部服務
JSONP是服務器與客戶端跨源通訊的經常使用方法,簡單適用,兼容性好。
基本原理:
①網頁添加一個<script>
元素,向服務器請求一個腳本,直接做爲代碼運行,這不受同源政策限制,能夠跨域請求。
②服務器收到請求後,拼接一個字符串,將 JSON 數據放在函數名裏面,做爲字符串返回(bar({...})
)
③客戶端會將服務器返回的字符串,做爲代碼解析,由於瀏覽器認爲,這是<script>
標籤請求的腳本內容。這時,客戶端只要定義了bar()
函數,就能在該函數體內,拿到服務器返回的 JSON 數據。
//請求的腳本網址有一個callback參數(?callback=bar),用來告訴服務器,客戶端的回調函數名稱(bar) <script src="http://api.foo.com?callback=bar"></script> //定義bar()函數,在該函數體內,拿到服務器返回的 JSON 數據 function foo(data) { console.log('服務器返回:' + data.id); }; //服務器收到這個請求之後,會將數據放在回調函數的參數位置返回。 foo({ 'ip': '8.8.8.8' });
缺點:只能get請求
WebSocket協議是一種基於TCP的網絡協議,取代用HTTP做爲傳輸層的雙向通信技術——容許服務器主動發送信息給客戶端。使用ws://
(非加密)和wss://
(加密)做爲協議前綴。
①服務器根據WebSocket請求頭的Origin
字段(表示:請求源自哪一個域名),判斷是否許可本次通訊
②若是該域名在白名單內,服務器就會作出迴應,因此沒有同源限制
//websocket請求頭(摘自網絡) GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com //判斷爲白名單後,服務端作出迴應 HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫,是W3C標準,是跨源AJAX請求的根本解決方法。
GET
請求,CORS容許任何類型的請求。application/x-www-form-urlencoded
、multipart/form-data
、text/plain
)的結合。Origin
字段。Origin
字段的值。//****JavaScript腳本: var url = 'http://api.alice.com/cors'; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.withCredentials = true;/*在 AJAX 請求中打開withCredentials屬性,從而向服務器發送 Cookies。可是,若是服務器端的響應中未攜帶 Access-Control-Allow-Credentials: true ,瀏覽器將不會把響應內容返回給請求的發送者。*/ xhr.send(); //***請求頭: GET /cors HTTP/1.1 Origin: http://api.bob.com /*表示請求來自哪一個域(協議 + 域名 + 端口)。服務器根據這個值,決定是否贊成此次請求*/ Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0... //****若是Origin指定的域名在許可範圍內,服務端響應頭之中,會多出幾個頭信息字段,有三個與 CORS 請求相關的字段,都以Access-Control-開頭。 Access-Control-Allow-Origin: http://api.bob.com /*必須字段。值能夠是請求頭Origin字段的值(若是服務端指定了具體的Origin域名,則響應頭中的 Vary 字段的值必須包含 「Origin」,告訴客戶端:服務器對不一樣的源站返回不一樣的內容),對於不須要攜帶身份憑證的請求(請求頭沒有攜帶cookie信息),服務器能夠指定該字段的值爲一個*,表示接受任意域名的請求。*/ Access-Control-Allow-Credentials: true /*可選字段。表示服務器明確許可瀏覽器能夠把 Cookie 包含在請求中,一塊兒發給服務器。但,須 AJAX 請求中打開withCredentials屬性才起做用*/ Access-Control-Expose-Headers: FooBar /*在跨域訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,若是要訪問其餘頭,則須要服務器設置本響應頭,把容許瀏覽器訪問的頭放入白名單*/ Content-Type: text/html; charset=utf-8
PUT
或DELETE
,或者Content-Type
字段的類型是application/json
。Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
字段之後,確認容許跨源請求,就能夠作出迴應。Origin
頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin
頭信息字段。//****JavaScript腳本: var url = 'http://api.alice.com/cors'; var xhr = new XMLHttpRequest(); xhr.open('PUT', url, true); xhr.setRequestHeader('X-Custom-Header', 'value'); xhr.send(); //****「預檢」請求頭 OPTIONS /cors HTTP/1.1 /*「預檢」請求用的請求方法是OPTIONS,表示這個請求是用來詢問的*/ Origin: http://api.bob.com /*表示請求來自哪一個源*/ Access-Control-Request-Method: PUT /*該字段是必須的,用來列出瀏覽器的 CORS 請求會用到哪些 HTTP 方法,本例是PUT*/ Access-Control-Request-Headers: X-Custom-Header /*該字段是一個逗號分隔的字符串,指定瀏覽器 CORS 請求會額外發送的頭信息字段,本例是X-Custom-Header*/ Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0... ①//****服務器否認了「預檢」請求的響應頭: OPTIONS http://api.bob.com HTTP/1.1 Status: 200 Access-Control-Allow-Origin: https://notyourdomain.com/*明確不包括髮出請求的http://api.bob.com*/ Access-Control-Allow-Method: POST //****瀏覽器發現服務器不一樣意預檢請求,觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制檯報錯信息: XMLHttpRequest cannot load http://api.alice.com. Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin. ②//****服務器容許了「預檢」請求的迴應頭: HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://api.bob.com /*表示http://api.bob.com能夠請求數據。該字段也能夠設爲星號,表示贊成任意跨源請求。*/ Access-Control-Allow-Methods: GET, POST, PUT /*該字段必需,它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。注意,返回的是全部支持的方法,而不單是瀏覽器請求的那個方法。這是爲了不屢次「預檢」請求。*/ Access-Control-Allow-Headers: X-Custom-Header /*若是瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段,不限於瀏覽器在「預檢」中請求的字段。*/ Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain Access-Control-Max-Age: 1728000 /*該字段可選,用來指定本次預檢請求的有效期,單位爲秒。這裏有效期是20天(1728000秒),即容許緩存該條迴應1728000秒(即20天),在此期間,不用發出另外一條預檢請求。*/ //****「預檢」請求經過以後,瀏覽器的會再發一個正常 CORS 請求: PUT /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com X-Custom-Header: value Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0... //****而後,服務器正常的迴應: Access-Control-Allow-Origin: http://api.bob.com Content-Type: text/html; charset=utf-8
缺點:兼容性很差(>ie10)
具體參見:https://wangdoc.com/javascript/bom/cors.html
詳情查看:https://wangdoc.com/javascript/bom/same-origin.html
其餘相關連接:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage、https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS