跨域, 是指調用方和被調用方的協議、域名和端口,不徹底相同(即不一樣源),跨域的訪問或調用會被瀏覽器禁止,於是跨域是瀏覽器端特有的,服務端無跨域問題。javascript
服務端代理,又稱爲請求轉發/反向代理php
原理:html
A站的頁面跨域訪問B站的服務端接口,A站請求本身的後端接口,A站的後端接口負責轉發請求到B站,並將B站的響應返回給A站頁面。前端
適用場景:java
調用第三方服務接口ajax
優勢:
比較通用,可解決大多數跨域問題json
缺點:
須要後端支持(提供轉發接口),增長本站服務器負擔,沒法維持session狀態後端
系統A(A.com)對外提供一個郵件發送接口,可接收GET或POST方式傳遞參數(如email, subject, body等)數據。當系統b(b.com)須要使用到這個接口的功能時,並提供比較好的用戶體驗時,採起了經過ajax將用戶填入的信息傳遞數據給這個接口,來實現發送郵件的目的。api
系統b的頁面請求自身的後端接口proxy.php跨域
$.post( 'http://b.com/proxy.php', { email:"impng@impng.com", subject:"ajax跨域之代理", body:"這裏是郵件內容" }, function(data){ if(data == 1) alert('發送成功!'); } );
proxy.php轉發請求到系統A
$post = $_POST; $data = array( 'email' => $post['email'], 'subject' => $post['subject'], 'body' => $post['body'], ); $url = 'http://A.com/ISendMail.php?'; // 系統A的發送郵件接口 $url .= http_build_query($data); $res = file_get_contents($url); echo $res;
原理:
因爲引用外部資源不受同源策略限制,如 <img>
<link>
<script>
, 因此可經過動態插入<script>
標籤, 訪問跨域的服務接口,只要跨域接口把數據組裝成函數調用的形式,前端頁面就能夠獲取到數據.
優勢:
徹底由前端控制,不須要後端支持,兼容性好。
缺點:
只能get方式(由於本質是加載js)
示例:
<!doctype html> <html> <head> <script> function loadContent() { var s = document.createElement('SCRIPT'); s.src = 'http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent'; //指定回調 document.body.appendChild(s); } function setDivContent(v) { var dv = document.getElementById("dv"); dv.innerHTML = v; } </script> </head> <body> <div id="dv"></div> <input value="Click Me"> </body> </html> // 其中的www.anotherdomain.com/TestCrossJS.aspx是這樣的, <script runat="server"> void Page_Load(object sender, EventArgs e) { string f = Request.QueryString["f"]; Response.Clear(); Response.ContentType = "application/x-javascript"; Response.Write(String.Format(@" {0}('{1}'); ", f, DateTime.Now)); //組裝函數調用表達式 Response.End(); } </script>
在頁面內嵌或動態生成指向別的網站的IFRAME,而後這2個網頁間能夠經過改變對方的anchor hash fragment來傳輸消息。改變一個網頁的anchor hash fragment並不會使瀏覽器從新加載網頁,因此一個網頁的狀態得以保持,而網頁自己則能夠經過一個計時器(timer)來察覺本身anchor hash的變化(h5 window.onhashchange),從而獲取數據並做出響應。
父頁面
<!doctype html> <html> <head> <meta charset="utf8" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <title>Test iframe cross domain</title> <script> var url = "http://localhost:8081/cross_sub.html"; var oldHash = null; var timer = null; function getHash() { var hash = window.location.hash; if ((hash.length >= 1) && (hash.charAt(0) == '#')) { hash = hash.substring(1); //得到#後的值 } return hash; } function sendRequest() { var d = document; //簡寫document var t = d.getElementById('request'); var f = d.getElementById('ifrm'); f.src = url + "#" + t.value + (new Date().getTime());//修改iframe.src的hash } function setDivHtml(v) { var d = document; var dv = d.getElementById('response'); dv.innerHTML = v; } function idle() { var newHash = getHash(); if (newHash != oldHash) { setDivHtml(newHash); oldHash = newHash; } timer = window.setTimeout(idle, 100); // 遞歸實現循環 定時檢查自身hash的變化 } window.onload = function (){ timer = window.setTimeout(idle, 100); }; window.name = 'parent'; </script> </head> <!-- localhost:8080/cross_parent.html --> <body> <h1>THIS IS THE PARENT PAGE</h1> <input type="text" id="request" /> <input type="submit" value="send" id="send" /> <div id="response" style="margin: 30px; padding: 30px; border:1px dashed #ccc;"> </div> <iframe src="http://localhost:8081/cross_sub.html" frameborder="0" id="ifrm" style="border:5px solid #ddd; padding:20px; width:80%; margin:auto; display:block; height:300px; "></iframe> <script type="text/javascript"> var sendBtn = document.getElementById('send'); sendBtn.onclick = sendRequest; </script> </body> </html>
子頁面
<!doctype html> <html> <head> <meta charset="utf8" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <title>Test iframe cross domain</title> <script> var url = "http://localhost:8080/cross_parent.html"; var oldHash = null; var timer = null; function getHash() { var hash = window.location.hash; if ((hash.length >= 1) && (hash.charAt(0) == '#')) { hash = hash.substring(1); } return hash; } function sendRequest() { var d = document; var t = d.getElementById('request'); var f = parent; //alert(f.document); //試着去掉這個註釋,你會獲得「Access is denied」 f.location.href = url + "#" + t.value +( new Date().getTime() ); //修改父窗口的hash } function setDivHtml(v) { var d = document; var dv = d.getElementById('response'); dv.innerHTML = v; } function idle() { var newHash = getHash(); if (newHash != oldHash) { setDivHtml(newHash); oldHash = newHash; } timer = window.setTimeout(idle, 100); } window.onload = function() { timer = window.setTimeout(idle, 100); }; window.name = 'sub'; </script> </head> <!-- localhost:8081/cross_sub.html --> <body> <h1>THIS IS THE SUB PAGE</h1> <input type="text" id="request" /> <input type="submit" value="send" id="send" /> <div id="response" style="margin: 30px; padding: 30px; border:1px dashed #ccc;"> </div> <script type="text/javascript"> var sendBtn = document.getElementById('send'); sendBtn.onclick = sendRequest; </script> </body> </html>
window 對象的name屬性是一個很特別的屬性,當該window的location變化,而後從新加載,它的name屬性能夠依然保持不變。那麼咱們能夠在頁面 A中用iframe加載其餘域的頁面B,而頁面B中用JavaScript把須要傳遞的數據賦值給window.name,iframe加載完成以後,頁面A修改iframe的地址,將其變成同域的一個地址,而後就能夠讀出window.name的值了。這個方式很是適合單向的數據請求,並且協議簡單、安全。不會像JSONP那樣不作限制地執行外部腳本。
父頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>window.name test</title> </head> <body> <button onclick="toggleIfrmSrc()">切換iframe src</button> <iframe id="ifrm" src="http://localhost:9200/a.html" frameborder="0" style="width: 500px; height: 500px;"></iframe> <script> var $ = function(s ) { return document.querySelector(s); } function toggleIfrmSrc() { if (!oldUrl) { oldUrl = ifrm.src; } var isCross = /:9200/.test(ifrm.src); var newUrl = isCross ? '/a-same.html' : oldUrl; ifrm.src = newUrl; console.log('subwin.name:', subwin.name); // 獲取到 } var oldUrl = ''; var ifrm = $('#ifrm'); var subwin = ifrm.contentWindow; console.log('subwin.name:', subwin.name); // 跨域,獲取不到 </script> </body> </html>
子頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>window.name test </title> </head> <body> <h2>sub page, window.name</h2> <script> window.name = 'iamA'; var c = 1; setInterval(function () { window.name += c; }, 2000); </script> </body> </html>
僅僅是子域名不一樣,可改成同域。如:blog.exam.com, exam.com blog.examp.com/hello.html中,可設置 document.domain = 'exam.com';
h5 api
需後端支持