最常用的兩種跨域解決方案

同源策略:

是由NetScape提出的著名的安全策略,所有支持javaScript的瀏覽器都使用這個策略。同源策略限制了一個源中加載文本或腳本與來自其它源中資源的交互方式。

IE特例:

  • 授信范圍(Trust Zones):兩個相互之間高度互信的域名,如公司域名(corporate domains),不遵守同源策略的限制。

  • 端口:IE未將端口號加入到同源策略的組成部分之中,因此 http://company.com:81/index.htmlhttp://company.com/index.html 屬于同源并且不受任何限制。

以上參考自MDN

所謂同源是指:域名、協議、端口都相同。同源策略是瀏覽器最核心也最基本的安全功能
這是知乎上關于同源策略的一篇文章。。但是瀏覽器在安全性和實用性上做出了讓步,img/script/style/iframe等有src屬性的都可以跨域引用資源。

方案一——通過JSONP跨域

jsonp是JSON with padding的簡寫,看起來與json差不多,但是包含在函數調用中的json,利用動態script元素來使用(具有src屬性的如img,iframe,srcipt都不受同源策略的影響)。該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,然后服務端返回數據時會將這個callback參數作為函數名來包裹住JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。

如果使用jquery,可以在type為get的時候dataType設為jsonp,就可以了。

$.ajax({
        url: 'http://www.qdaily.com/get_user_and_radar.json?winWidth=1280&winHeight=800',
        type: 'get',
        dataType: 'jsonp',
        data: {},
    })
    .done(function(data) {
        console.log(data.status);
    })

然后,很幸運的就可以看到這個:

Paste_Image.png

但是可以看出其實是請求到數據的,只是沒有執行我們的callBack函數。

Paste_Image.png

因為前面有提到:服務端返回數據時會將這個callback參數作為函數名來包裹住JSON數據,也就是服務端的返回需要拼接成這樣(比如函數名叫做haddleData):

haddleData({"status":true,subscribes:["12","23"]})

另外:jquery的ajax方法可以傳入兩個參數

jsonp : "callback"http://設置這個會替換瀏覽器發送請求時地址后面自動添加的?callback=xxx中的callback這個字,一般情況下不用傳這個參數
jsonpCallback: "success_jsonpCallback"http://這個值將用來取代jQuery自動生成的隨機函數名,也就是上句話中的'xxx'。

【這兒遺留了一個問題,還請各位解決】如果jsonpCallback不手動設置的話,jquery是會自動生成個隨機的字符串的,但是服務器那邊的函數名要寫什么,回調函數才會正確的執行呢?

看一個運行成功的實例:

$.ajax({
        url: 'http://localhost:8000/remote/remote.js',
        type: 'get',
        dataType: 'jsonp',
        jsonp: 'back',
        jsonpCallback: "haddleData",
        data: {},
    })
    .done(function(data) {
        console.log(data.status);
    })
//這段程序運行在8080端口的

remote.js作為遠端其中寫了句

haddleData({"status":true,subscribes:["12","23"]})
Paste_Image.png

對于大部分js初學者,大多使用jquery,而對原生不太熟悉。但是須知,ajax和jsonp其實本質上是不同的東西,ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標簽來調用服務器提供的js腳本,利用腳本下載下來后立即執行的事實實現callback方法的調用。

原生js的jsonp簡單實現:

<script>
    function haddleData(data){
        console.log(data.status);
    }
</script>
<script type="text/javascript" src="http://localhost:8000/remote/remote.js"></script>

當然有多個本地函數需要處理的時候,加上回調函數名才是方便的,像這樣:

<script type="text/javascript" src="http://localhost:8000/remote/remote.js?callback=haddleData"></script>

加上callback參數,可以使得服務器端根據前端指定的方法名cb動態返回cb(data);而不是都寫死handleData(data);

【總結下jsonp】:

  • 優點:
    簡單,函數回調在本地處理;
  • 缺點:
    1、安全性(存在注入漏洞,如CSRF,XSS);
    2、如果出現錯誤,不會像http請求那樣有狀態碼;
    3、只能使用get請求;

方案二——CORS(Cross-Origin Resource Sharing)

這是一個W3C標準(顯然比jsonp背景深厚許多),同樣需要瀏覽器和服務器同時支持,但是整個通信過程,都是瀏覽器自動完成,不需要用戶參與,就像平時寫Ajax一樣(如果使用的是jquery的話)。
下面是原生js實現CORS的代碼

    function createCORSRequest(method,url){
        var xhr=new XMLHttpRequest();
        if("withCredentials" in xhr){
            xhr.open(method,url,true);
        }else if(typeof XDomainRequest != "undefined"){//IE10之前的版本使用XDmainRequest支持CORS
            xhr=new XDomainRequest();
            xhr.open(method,url);
        }else{
            xhr=null;
        }
        return xhr;
    }
    var request=createCORSRequest("get","待訪問的地址");
    if(request){
        request.onload=function(data){
            //do sth
        };
        request.send();
    }   

適用場景:

承載的信息量大,get形式搞不定,需選用post傳輸。CORS支持所有類型的傳輸。

兼容性:

移動端全面支持(除opera mini),PC上IE8+。

CORS思想:

使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功還是失敗。(請求和響應都不包含cookie)

當然如果設置成下面這樣,所有的跨域都可以實現了,但這樣畢竟太不安全。
"Access-Control-Allow-Origin:*";//允許任何域向我們的服務器發送請求

一般情況下,瀏覽器發送一個額外的Origin頭部(由瀏覽器自動生成發送)

Origin:http://localhost:8080//本地網址

然后由服務器發送一個響應表頭:Access-Control-Allow-Origin,如果服務器接收該請求,返回值(只能是通配符或單域名。)就和請求值一樣。

Access-Control-Allow-Origin:http://localhost:8080

=>CORS方案的重點其實就在于服務器端的配置。

簡單請求

  • 只使用 GET, HEAD 或者 POST 請求方法。

如果使用 POST 向服務器端傳送數據,則數據類型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一種。

  • 不會使用自定義請求頭(類似于 X-Modified 這種)。

HTTP頭部信息不超出以下{Accept,Accept-Language,Content-Language,Last-Event-ID,content-type(只限于上面提到的3種類型)}

對于簡單請求,瀏覽器直接發出CORS請求。瀏覽器會自動在頭信息(Request Headers)中,添加一個Origin 字段,來表明本次請求來自哪個域。

Paste_Image.png

如果這個源不在許可范圍內,會報錯: No 'Access-Control-Allow-Origin' header is present on the requested resource.

如果Origin指定的域名在許可范圍內(必須是跨域了的),Response Headers中會多出幾個頭信息字段。

Access-Control-Allow-Credentials:true//值為true表示允許發送cookie
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:http://localhost:8080
Access-Control-Max-Age:1728000
withCredentials屬性

因為CORS默認不發送cookie和http認證,如果要把Cookie發到服務器,就要指定Access-Control-Allow-Credentials:true;
另外AJAX中也要打開withCredentials屬性。

var xhr=new XMLHttpRequest();
xhr.withCredentials=true;

jquery ajax請求參數中加入

xhrFields: {
  withCredentials: true
}

非簡單請求

除了上面說的簡單請求外都是非簡單請求,比如:請求方法是PUT
或DELETE,或者Content-Type字段的類型是application/json,又或者有自定義請求頭Access-Control-Request-Headers: X-Custom-Header。

比如,我添加自定義請求頭

xhr.setRequestHeader('Some-Custom-Response-Header', 'value');

就會發現連續向同一地址請求了兩次,而第一次請求什么值也沒拿到

Paste_Image.png

第二次請求

Paste_Image.png

這是因為瀏覽器發現,這是一個非簡單請求,就自動發出一個"預檢"請求,要求服務器確認可以這樣請求。"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。"預檢"請求之后,瀏覽器球會進行正常CORS請求。

服務器端配置(rails為例)

1、可以參考這個方法:為 RESTful API 配置 CORS 實現跨域請求,寫的挺詳細的。然而對于rails并不算熟悉的我來說,有gem包,怎么可能放著不用呢~
2、這是Rack CORS 中間件的項目地址,按照文檔的說明1分鐘就可以基本搞定。

【問題:】

當我請求方式為put時,卻出現了404錯誤,是因為沒有為options提供路由么?

rails中使用CORS中的功能和細節還有很多,等到需要時再行挖掘吧,歡迎使用過的各位一起交流問題。

【參考】

http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=402804565&idx=1&sn=415fc9eab30edcb66227ed5e099f9a66&scene=1&srcid=0413psVAfbsElTaWvrVas6xM#rd
http://www.ruanyifeng.com/blog/2016/04/cors.html
http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
http://www.cnblogs.com/know/archive/2011/10/09/2204005.html
http://blog.csdn.net/xiaoxian8023/article/details/27817861

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容