背景
?最近做項目經常碰到跨域的問題,總在聯調中浪費了不少時間,當然若時間充裕的話,跨域基本都是可以通jsonp來解決問題(get方式的請求),只是因為對接環境問題,就打算在請求頭設置參數來解決跨域問題。
跨域資源共享 CORS 原理
?CORS定義一種跨域訪問的機制,可以讓AJAX實現跨域訪問。CORS 允許一個域上的網絡應用向另一個域提交跨域 AJAX 請求。實現此功能非常簡單,只需由服務器發送一個響應標頭即可。這種方案也是我們最長用的,也是比較簡單的。
例子如下:
JAVA后端代碼:
response.setHeader("Access-Control-Allow-Origin", "http://www.xxx.com");
response.setHeader("Access-Control-Allow-Credentials", "true");
(1)Access-Control-Allow-Origin
表示接受任意域名的請求,如果是多個域名中間用','隔開,如果是 * 表示允許任何域名
(2)Access-Control-Allow-Credentials
該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設為true,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。這個值也只能設為true,如果服務器不要瀏覽器發送Cookie,刪除該字段即可。
(3)Access-Control-Expose-Headers
該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值
(4)Access-Control-Allow-Credentials
CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials字段。另外發送請求方需要varxhr=newXMLHttpRequest();? ?xhr.withCredentials=true; 否則,即使服務器同意發送Cookie,瀏覽器也不會發送。或者,服務器要求設置Cookie,瀏覽器也不會處理。但是,如果省略withCredentials設置,有的瀏覽器還是會一起發送Cookie。這時,可以顯式關閉withCredentials。
然而我們經常設置這樣的選項,還是會在瀏覽器彈出:XMLHttpRequest cannot load http:/www.xxx.com.Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin. 這樣的字眼,而且在post請求復現高。
這個時候多是因為瀏覽器發送了有一個預檢請求,"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息里面,關鍵字段是Origin,表示請求來自哪個源。
在預檢請求會帶兩個參數
(1)Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段
服務器收到"預檢"請求以后,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。
所以如果是post的請求獲取其他非簡單的請求的話,我們可以嘗試在請求頭中加上
response.setHeader(Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");(注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次"預檢"請求。)
res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
就能解決上述的即使加了請求頭允許跨域的域名以及Cookie的身份驗證,依舊出現跨域的問題。
由于CORS請求和瀏覽器有關,所以在低版本IE或者其他的流量器如果不支持,該方式是行不通的,這個時候就只能使用JsonP的方式來解決跨域。
JSONP解決跨域原理
JSONP(JSON with Padding)是JSON的一種”使用模式”,可用于解決主流瀏覽器的跨域數據訪問的問題。由于同源策略,一般來說位于 server1.example.com 的網頁無法與不是 server1.example.com的服務器溝通,而 HTML 的script元素是一個例外。利用 標簽<script>元素的這個開放策略,網頁可以得到從其他來源動態產生的 JSON 資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料并不是 JSON,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析。?元素的這個開放策略,網頁可以得到從其他來源動態產生的 JSON 資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料并不是 JSON,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析。
通俗的來講就是:JSONP的最基本的原理是:動態添加一個標簽,而script標簽的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關了。標簽,而script標簽的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關了。
下面是簡單的dome? 由于訪問本地沒有跨域,這里只是講解:
紅色框內是一個ajax的跨域請求,假設前端請求的是一個不在當前域地址,這個時候瀏覽器會出現跨域的錯誤提示;
于是我們用,<script>標簽的特性來解決這問題:
這樣的話,就能正常訪問跨域的請求接口了。
兩者相比:
Jsonp不會因為瀏覽器原因而不支持跨域,但是jsonp只是支持get方式的請求,所以兩者相比的話,因環境而定,并沒有誰好誰劣