1. 程式人生 > >詳解跨域(最全的解決方案)

詳解跨域(最全的解決方案)

1. 什麼是跨域

跨域,是指瀏覽器不能執行其他網站的指令碼。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實施的安全限制。

同源策略限制了一下行為:

  • Cookie、LocalStorage 和 IndexDB 無法讀取
  • DOM 和 JS 物件無法獲取
  • Ajax請求傳送不出去
2. 常見的跨域場景

所謂的同源是指,域名、協議、埠均為相同。

http://www.nealyang.cn/index.html 呼叫   http://www.nealyang.cn/server.php  非跨域

http://www.nealyang.cn/index.html 呼叫   http://www.neal.cn/server.php  跨域,主域不同
http://abc.nealyang.cn/index.html 呼叫 http://def.neal.cn/server.php 跨域,子域名不同 http://www.nealyang.cn:8080/index.html 呼叫 http://www.nealyang.cn/server.php 跨域,埠不同 https://www.nealyang.cn/index.html 呼叫 http://www.nealyang.cn/server.php 跨域,協議不同 localhost 呼叫127.0.0.1跨域
3. 跨域的解決辦法

3.1 jsonp跨域

jsonp跨域其實也是JavaScript設計模式中的一種代理模式。在html頁面中通過相應的標籤從不同域名下載入靜態資原始檔是被瀏覽器允許的,所以我們可以通過這個“犯罪漏洞”來進行跨域。一般,我們可以動態的建立script標籤,再去請求一個帶參網址來實現跨域通訊

//原生的實現方式
let script = document.createElement('script');

script.src ='http://www.nealyang.cn/login?username=Nealyang&callback=callback';

document.body.appendChild(script);function callback(res){
  console.log(res);}

當然,jquery也支援jsonp的實現方式

$.ajax({
    url:'http://www.nealyang.cn/login',
    type:'GET',
    dataType
:'jsonp',//請求方式為jsonp jsonpCallback:'callback', data:{"username":"Nealyang"}})

雖然這種方式非常好用,但是一個最大的缺陷是,只能夠實現get請求

3.2 document.domain + iframe 跨域

這種跨域的方式最主要的是要求主域名相同。什麼是主域名相同呢?
www.geekjc.com aaa.geekjc.com ba.ad.geekjc.com 這三個主域名都是geekjc.com,而主域名不同的就不能用此方法。

假設目前a.geekjc.com 和 b.geekjc.com 分別對應指向不同ip的伺服器。

a.geekjc.com 下有一個test.html檔案

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>html</title><scripttype="text/javascript"src="jquery-1.12.1.js"></script></head><body><div>A頁面</div><iframestyle="display : none"name="iframe1"id="iframe"src="http://b.geekjc.com/1.html"frameborder="0"></iframe><scripttype="text/javascript">
        $(function(){try{
                document.domain ="geekjc.com"}catch(e){}
            $("#iframe").load(function(){var jq = document.getElementById('iframe').contentWindow.$
                jq.get("http://geekjc.com/test.json",function(data){
                    console.log(data);});})})</script></body></html>

利用 iframe 載入 其他域下的檔案(geekjc.com/1.html), 同時 document.domain 設定成 geekjc.com ,當 iframe 載入完畢後就可以獲取 geekjc.com 域下的全域性物件, 此時嘗試著去請求 geekjc.com 域名下的 test.json (此時可以請求介面),就會發現資料請求失敗了~~ 驚不驚喜,意不意外!!!!!!!

資料請求失敗,目的沒有達到,自然是還少一步:

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>html</title><scripttype="text/javascript"src="jquery-1.12.1.js"></script><scripttype="text/javascript">
        $(function(){try{
                document.domain ="geekjc.com"}catch(e){}})</script></head><body><divid="div1">B頁面</div></body></html>

此時在進行重新整理瀏覽器,就會發現資料這次真的是成功了~

3.3 window.name + iframe 跨域

window.name屬性可設定或者返回存放視窗名稱的一個字串。他的神器之處在於name值在不同頁面或者不同域下載入後依舊存在,沒有修改就不會發生變化,並且可以儲存非常長的name(2MB)

假設index頁面請求遠端伺服器上的資料,我們在該頁面下建立iframe標籤,該iframe的src指向伺服器檔案的地址(iframe標籤src可以跨域),伺服器檔案裡設定好window.name的值,然後再在index.html裡面讀取改iframe中的window.name的值。完美~

<body><scripttype="text/javascript"> 
    iframe = document.createElement('iframe'),
    iframe.src ='http://localhost:8080/data.php';
    document.body.appendChild(iframe);
    iframe.onload =function(){
      console.log(iframe.contentWindow.name)};</script></body>

當然,這樣還是不夠的。

因為規定如果index.html頁面和和該頁面裡的iframe框架的src如果不同源,則也無法操作框架裡的任何東西,所以就取不到iframe框架的name值了,告訴你我們不是一家的,你也休想得到我這裡的資料。 既然要同源,那就換個src去指,前面說了無論怎樣載入window.name值都不會變化,於是我們在index.html相同目錄下,新建了個proxy.html的空頁面,修改程式碼如下:

<body><scripttype="text/javascript"> 
    iframe = document.createElement('iframe'),
    iframe.src ='http://localhost:8080/data.php';
    document.body.appendChild(iframe);
    iframe.onload =function(){
      iframe.src ='http://localhost:81/cross-domain/proxy.html';
      console.log(iframe.contentWindow.name)};</script></body>

理想似乎很美好,在iframe載入過程中,迅速重置iframe.src的指向,使之與index.html同源,那麼index頁面就能去獲取它的name值了!但是現實是殘酷的,iframe在現實中的表現是一直不停地重新整理, 也很好理解,每次觸發onload時間後,重置src,相當於重新載入頁面,又觸發onload事件,於是就不停地重新整理了(但是需要的資料還是能輸出的)。修改後程式碼如下:

<body><scripttype="text/javascript"> 
    iframe = document.createElement('iframe');
    iframe.style.display ='none';var state =0;

    iframe.onload =function(){if(state ===1){var data = JSON.parse(iframe.contentWindow.name);
          console.log(data);
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
        document.body.removeChild(iframe);}elseif(state ===0){
          state =1;
          iframe.contentWindow.location ='http://localhost:81/cross-domain/proxy.html';}};

    iframe.src ='http://localhost:8080/data.php';
    document.body.appendChild(iframe);</script></body>

所以如上,我們就拿到了伺服器返回的資料,但是有幾個條件是必不可少的:

  • iframe標籤的跨域能力
  • window.names屬性值在文件重新整理後依然存在的能力

3.4 location.hash + iframe 跨域

此跨域方法和上面介紹的比較類似,一樣是動態插入一個iframe然後設定其src為服務端地址,而服務端同樣輸出一端js程式碼,也同時通過與子視窗之間的通訊來完成資料的傳輸。

關於錨點相信大家都已經知道了,其實就是設定錨點,讓文件指定的相應的位置。錨點的設定用a標籤,然後href指向要跳轉到的id,當然,前提是你得有個滾動條,不然也不好滾動嘛是吧。

而location.hash其實就是url的錨點。比如https://www.geekjc.com#geekjcg的網址開啟後,在控制檯輸入location.hash就會返回#geekjc的欄位。

基礎知識補充完畢,下面我們來說下如何實現跨域

如果index頁面要獲取遠端伺服器的資料,動態的插入一個iframe,將iframe的src執行伺服器的地址,這時候的top window 和包裹這個iframe的子視窗是不能通訊的,因為同源策略,所以改變子視窗的路徑就可以了,將資料當做改變後的路徑的hash值載入路徑上,然後就可以通訊了。將資料加在index頁面地址的hash上, index頁面監聽hash的變化,h5的hashchange方法

<body><scripttype="text/javascript">function getData(url, fn){var iframe = document.createElement('iframe');
      iframe.style.display ='none';
      iframe.src = url;

      iframe.onload =function(){
        fn(iframe.contentWindow.location.hash.substring(1));
        window.location.hash ='';
        document.body.removeChild(iframe);};

      document.body.appendChild(iframe);}// get data from servervar url ='http://localhost:8080/data.php';
    getData(url,function(data){var jsondata = JSON.parse(data);
      console.log(jsondata.name +' '+ jsondata.age);});</script></body>

補充說明:其實location.hash和window.name都是差不多的,都是利用全域性物件屬性的方法,然後這兩種方法和jsonp也是一樣的,就是隻能夠實現get請求

3.5 postMessage跨域

這是由H5提出來的一個炫酷的API,IE8+,chrome,ff都已經支援實現了這個功能。這個功能也是非常的簡單,其中包括接受資訊的Message時間,和傳送資訊的postMessage方法。

傳送資訊的postMessage方法是向外界視窗傳送資訊

otherWindow.postMessage(message,targetOrigin);

otherWindow指的是目標視窗,也就是要給哪一個window傳送訊息,是window.frames屬性的成員或者是window.open方法建立的視窗。 Message是要傳送的訊息,型別為String,Object(IE8、9不支援Obj),targetOrigin是限定訊息接受範圍,不限制就用星號 *

接受資訊的message事件

var onmessage =function(event){var data =event.data;var origin =event.origin;}if(typeof window.addEventListener !='undefined'){
    window.addEventListener('message',onmessage,false);}elseif(typeof window.attachEvent !='undefined'){
    window.attachEvent('onmessage', onmessage);}

舉個栗子

a.html(http://www.nealyang.cn/a.html)

<iframeid="iframe"src="http://www.neal.cn/b.html"style="display:none;"></iframe><script>var iframe = document.getElementById('iframe');
    iframe.onload =function(){var data ={
            name:'aym'};// 向neal傳送跨域資料
        iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.neal.cn');};// 接受domain2返回資料
    window.addEventListener('message',function(e){
        alert('data from neal ---> '+ e.data);},false);</script>

b.html(http://www.neal.cn/b.html)

<script>// 接收domain1的資料
    window.addEventListener('message',function(e){
        alert('data from nealyang ---> '+ e.data);var data = JSON.parse(e.data);if(data){
            data.number =16;// 處理後再發回nealyang
            window.parent.postMessage(JSON.stringify(data),'http://www.nealyang.cn');}},false);</script>

3.6 跨域資源共享 CORS

因為是目前主流的跨域解決方案。所以這裡多介紹點。

簡介

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。 它允許瀏覽器向跨源伺服器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。IE8+:IE8/9需要使用XDomainRequest物件來支援CORS。

整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。 因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。

兩種請求

說起來很搞笑,分為兩種請求,一種是簡單請求,另一種是非簡單請求。只要滿足下面條件就是簡單請求

  • 請求方式為HEAD、POST 或者 GET
  • http頭資訊不超出一下欄位:Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限於三個值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
    為什麼要分為簡單請求和非簡單請求,因為瀏覽器對這兩種請求方式的處理方式是不同的。

簡單請求

基本流程

對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭資訊之中,增加一個Origin欄位。 下面是一個例子,瀏覽器發現這次跨源AJAX請求是簡單請求,就自動在頭資訊之中,新增一個Origin欄位。

GET 
            
           

相關推薦

(解決方案)

1. 什麼是跨域跨域,是指瀏覽器不能執行其他網站的指令碼。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實施的安全限制。同源策略限制了一下行為:Cookie、LocalStorage 和 IndexDB 無法讀取DOM 和 JS 物件無法獲取Ajax請求傳送不出

AJAX解決方案(轉載)

題綱 關於跨域,有N種類型,本文只專注於ajax請求跨域(,ajax跨域只是屬於瀏覽器"同源策略"中的一部分,其它的還有Cookie跨域iframe跨域,LocalStorage跨域等這裡不做介紹),內容大概如下: 什麼是ajax跨域 原理 表現(整理了一些遇

vue開發過程中簡單解決方案

前言:我們在進行一個專案開發工程中,需要從後端工程師那裡獲取資料庫中的資料。然而前端程式碼和後端程式碼在未打包前是分離的,這就引入了一個“跨域取資料”的問題。 下面,我們就簡單說下,利用vue腳手架生成的專案,在開發過程中怎麼解決此問題。 第一步 找到並開啟config資料夾下的index.js,做如下配置

Git服務器安裝及安裝遇到問題解決方案【轉】

bsp erb 倉庫 .... gnu libcurl 執行 body ebs 轉自:http://www.cnblogs.com/grimm/p/5368777.html git是一個不錯的版本管理的工具。現在自己在搞一個簡單的應用程序開發,想使用git來進行管理。

掛載文件系統出現"kernel panic..." 史上解決方案

某個文件 table sha mount nic mic 2.6 完成 又是   問:掛載自己制作的文件系統卡在這裏:    NET: Registered protocol family 1    NET: Registered protocol family 17   

PHP Ajax 問題最佳解決方案

ajax 跨域 域名 -c php文件 解決 tle ron 跨域訪問 客戶端 本文通過設置Access-Control-Allow-Origin來實現跨域。 例如:客戶端的域名是client.runoob.com,而請求的域名是server.runoob.com。 如

.net webapi項目問題及解決方案

prot net 方案 ted access 節點 跨域訪問 架構師資料 eth 問題: 1.項目完成,部署到不同的iis版本上,跨域訪問有的通有的不通 解決辦法: 1.將復雜請求改為簡單請求 2.代碼中去掉所有跨域設置,配置中添加或修改節點 <system

關於ajax的一些解決方案

dst control 關於 請求 method request 進行 前端 請求方式 1、JSONP方式解決跨域問題 jsonp解決跨域問題是一個比較古老的方案(實際中不推薦使用),當然,在實際項目中如果要使用JSONP,一般會使用JQ等對JSONP進行了封裝的

Ajax原理及解決方案

一次 變化 mes iframe 實現 type .ajax 一個 min 跨域請求的產生 跨域請求歸根結底是由於瀏覽器的“同源策略”引起的,同源策略指的是域名相同、協議相同、端口相同, 假設有http://www.a.com/test.html,下面的示例 域名不同 h

Android熱修復技術原理(最新版本)

總結 核心 桌面圖標 實時 開源 穩定性 安卓 定義 check 本文框架 什麽是熱修復? 熱修復框架分類 技術原理及特點 Tinker框架解析 各框架對比圖 總結 ??通過閱讀本文,你會對熱修復技術有更深的認知,本文會列出各類框架的優缺點以及技術原理,文章末尾簡單描述

PHP ajax問題最佳解決方案

var cell clear 一定的 OS 添加 會有 request TP 一、本文通過設置Access-Control-Allow-Origin來實現跨域。 例如:客戶端的域名是client.runoob.com,而請求的域名是server.runoob.com。 如

PHP Ajax 問題最佳解決方案 【摘自菜鳥教程】

set color ray quest origin tty 所有 $origin con PHP Ajax 跨域問題最佳解決方案 分類 編程技術 http://www.runoob.com/w3cnote/php-ajax-cross-border.html 本文

Access to Image at 'file:///Users canvas本地圖片報錯解決方案

本地服務 canvas 解決方案 圖片 報錯 訪問 can 支持 ESS 1、設置跨域 添加跨域條件 crossorigin="anonymous" 前提是後端支持這個圖片跨域 2、上面加了之後還是報錯   如標題所示  

分享訪問的解決方案與基礎分析

什麼是跨域訪問? 由於瀏覽器同源策略,凡是傳送請求url的協議、域名、埠三者之間任意一個與當前頁面地址不同即為跨域。存在跨域的情況: 網路協議不同,如http協議訪問https協議。 埠不同,如80埠訪問8080埠。 域名不同,如qianduanblog.com訪問baidu.com。

如何安裝Nexus Repository Manager OSS 3.x,如何搭建管理Maven私服,win10、win7通用安裝,附:錯誤解決方案

        今天搭建一個Maven私服花了不少功夫,查閱了很多安裝的帖子以及百度了很多錯誤解決方案,然後將所有的帖子精華部分,附上我的經驗來帶給大家一個特別詳細的安裝方案,所以該文章大部分可以說是總結別人帖子。話不多說,開始安裝: 1.下載 &nb

.Net平臺下,處理問題CORE解決方案

VS2017 安裝兩個包:Install-PackageMicrosoft.AspNet.WebApi.Cors(如果build不過去還需安裝:Install-Package Microsoft.AspNet.WebApi-IncludePrerelease)  1、 

php 和ajax問題的解決方案

本文通過設定Access-Control-Allow-Origin來實現跨域。 例如:客戶端的域名是client.runoob.com,而請求的域名是server.runoob.com。 如果直接使用ajax訪問,會有以下錯誤: XMLHttpRequest cannot load http://s

jsonp原理()

什麼是JSONP? 先說說JSONP是怎麼產生的: 其實網上關於JSONP的講解有很多,但卻千篇一律,而且雲裡霧裡,對於很多剛接觸的人來講理解起來有些困難,著用自己的方式來闡釋一下這個問題,看看是否有幫助。 1、一個眾所周知的問題,Ajax直接請求普通檔案存在跨域無許可

JS:1.解決方案之-SpringMVC攔截器

package com.bdqn.utils; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResp

理解及常用解決方案

跨域,相信大家無論是在工作中還是在面試中經常遇到這個問題,常常在網上看到別人所整理的一些方法,看似知道是怎麼回事,但如果沒有動手實踐過,總覺得自己沒有真正的掌握,在這裡,通過自己認真思考整理一些常用的方法。 跨域的產生 不用多講,作為一名前端開發人員,相信大家都知道跨域是因為瀏覽器的同源策略所導致的