碼農小明要作一個展現業務數據的大屏給老闆看,裏面包含了來自本身網站的數據和來自隔壁老王的數據。
那麼本身網站的數據提供了 http://xiaoming.com/whoami 這樣的數據接口
隔壁老王提供了 http://oldwang.com/isdad 這樣的數據接口
單獨點開都是沒有問題的。可是一使用 js 的 ajax 請求就沒法收到來自 oldwang.com 的數據了。
點開瀏覽器控制檯一看,紅字標出(Chrome):javascript
XMLHttpRequest cannot load http://oldwang.com/isdad. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://xiaoming.com/' is therefore not allowed access.
這就是遇到了跨域問題php
想象一下若是隔壁老王根本不認識你,他的網站本身有各類用戶接口、訂單接口、文章接口,那麼誰均可以把這些接口返回的數據直接放在本身的網站上了,仍是實時的。前端
因此瀏覽器制定了一個 同源策略 ,限制了從一個源(origin)中的腳本獲取來自其餘源(origin)中的資源。java
若是兩個頁面擁有相同的協議(protocol: http),端口(port: 80),和主機(host: xiaoming.com),那麼這兩個頁面就屬於同一個源(origin)。jquery
這裏就不講多年前的iframe、flash等方式了,只講幾個最經常使用到的方案ajax
子域名不一樣也是會受到跨域限制的。這種問題最簡單,只須要將頁面聲明爲更高級的域就能夠了。sql
<script> document.domain = "x.com"; </script>
看到JSONP這個名字不少人覺得這是和JSON密切相關的一種用來跨域的黑科技,但實際上從跨域的視角看,跟JSON並無一毛錢關係,他是利用了瀏覽器容許跨域加載 js 等資源來獲取數據。json
由於瀏覽器支持跨域加載 js 如 <script src="http://aliyun.com/....."></script>
,因此很簡單,能夠把數據包裝成 js 就能夠了。後端
這是數據,經過 script 加載到數據沒法「執行」,更沒法傳給 ajax 的回調函數:api
{
"data": 123 }
這是js腳本,只要將 callback
與 ajax 的回調函數作關聯,就能夠講數據傳給回調函數:
callback({
"data": 123 })
這能夠看到兩點:
1、須要 callback 與 ajax 回調函數綁定;
2、須要數據服務器 配合 的。
3、只支持GET請求
前端若是用 jquery,jquery已經完成了整個取值過程的封裝,邏輯是:
1. 隨機生成不重複的 callback 函數名,並與 ajax 回調函數 綁定。
1. 將 callback 函數名放入 URL 的 query string 中,如http://oldwang.com/api?callback= jQuery214015557975923291245_1460532274390
1. 生成一個 <script>
標籤,將上述 URL 做爲 src
1. 等待數據加載,並把數據傳入 ajax 的回調函數
後端以 php 爲例,邏輯是獲取瀏覽器傳來一個參數做爲callback包裝數據:
<?php echo $_GET['callback']."(". $data .")"; ?>
他的原理是隔壁老王主動告訴瀏覽器「別攔着小明,咱們是親戚……」
因此最簡單的例子,就是在數據服務器返回的頭信息中包含:
Access-Control-Allow-Origin: http://xiaming.com
然而這個頭信息並不支持枚舉,那若是隔壁老王的親戚太多就只能經過程序來動態得生成這個頭信息了,以PHP爲例:
<?php if (is_my_bastard($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); } ?>
若是老王做爲一個好人,來者不拒。那麼能夠直接使用 *
Access-Control-Allow-Origin: *
CORS默認是不帶 cookie 信息的,若是要帶上 cookie 須要添加 withCredentials 參數,以 jquery 爲例:
$.ajax({ url: "http://laowang.com/isdad", xhrFields: { withCredentials: true } });
而服務器還須要加上容許 Credentials 的頭信息以及不容許用通配符「*」,以下面的代碼
<?php if (is_my_bastard($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); // 不容許用 * Access-Control-Allow-Credentials:true } ?>