背景
?最近做項目經(jīng)常碰到跨域的問題疗我,總在聯(lián)調(diào)中浪費了不少時間垮庐,當然若時間充裕的話银觅,跨域基本都是可以通jsonp來解決問題(get方式的請求),只是因為對接環(huán)境問題践宴,就打算在請求頭設置參數(shù)來解決跨域問題鲸匿。
跨域資源共享 CORS 原理
?CORS定義一種跨域訪問的機制,可以讓AJAX實現(xiàn)跨域訪問阻肩。CORS 允許一個域上的網(wǎng)絡應用向另一個域提交跨域 AJAX 請求带欢。實現(xiàn)此功能非常簡單,只需由服務器發(fā)送一個響應標頭即可。這種方案也是我們最長用的洪囤,也是比較簡單的徒坡。
例子如下:
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
該字段可選喇完。它的值是一個布爾值,表示是否允許發(fā)送Cookie剥啤。默認情況下锦溪,Cookie不包括在CORS請求之中。設為true府怯,即表示服務器明確許可刻诊,Cookie可以包含在請求中,一起發(fā)給服務器牺丙。這個值也只能設為true则涯,如果服務器不要瀏覽器發(fā)送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請求默認不發(fā)送Cookie和HTTP認證信息辫愉。如果要把Cookie發(fā)到服務器睹欲,一方面要服務器同意,指定Access-Control-Allow-Credentials字段一屋。另外發(fā)送請求方需要varxhr=newXMLHttpRequest();? ?xhr.withCredentials=true; 否則,即使服務器同意發(fā)送Cookie袋哼,瀏覽器也不會發(fā)送冀墨。或者涛贯,服務器要求設置Cookie诽嘉,瀏覽器也不會處理。但是,如果省略withCredentials設置虫腋,有的瀏覽器還是會一起發(fā)送Cookie骄酗。這時,可以顯式關閉withCredentials悦冀。
然而我們經(jīng)常設置這樣的選項趋翻,還是會在瀏覽器彈出:XMLHttpRequest cannot load http:/www.xxx.com.Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin. 這樣的字眼,而且在post請求復現(xiàn)高盒蟆。
這個時候多是因為瀏覽器發(fā)送了有一個預檢請求踏烙,"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的历等。頭信息里面讨惩,關鍵字段是Origin,表示請求來自哪個源寒屯。
在預檢請求會帶兩個參數(shù)
(1)Access-Control-Request-Method
該字段是必須的荐捻,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT寡夹。
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串处面,指定瀏覽器CORS請求會額外發(fā)送的頭信息字段
服務器收到"預檢"請求以后,檢查了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的身份驗證沼溜,依舊出現(xiàn)跨域的問題平挑。
由于CORS請求和瀏覽器有關,所以在低版本IE或者其他的流量器如果不支持系草,該方式是行不通的通熄,這個時候就只能使用JsonP的方式來解決跨域。
JSONP解決跨域原理
JSONP(JSON with Padding)是JSON的一種”使用模式”找都,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題唇辨。由于同源策略,一般來說位于 server1.example.com 的網(wǎng)頁無法與不是 server1.example.com的服務器溝通能耻,而 HTML 的script元素是一個例外赏枚。利用 標簽<script>元素的這個開放策略亡驰,網(wǎng)頁可以得到從其他來源動態(tài)產(chǎn)生的 JSON 資料,而這種使用模式就是所謂的 JSONP饿幅。用 JSONP 抓到的資料并不是 JSON凡辱,而是任意的JavaScript,用 JavaScript 直譯器執(zhí)行而不是用 JSON 解析器解析栗恩。?元素的這個開放策略透乾,網(wǎng)頁可以得到從其他來源動態(tài)產(chǎn)生的 JSON 資料,而這種使用模式就是所謂的 JSONP摄凡。用 JSONP 抓到的資料并不是 JSON续徽,而是任意的JavaScript,用 JavaScript 直譯器執(zhí)行而不是用 JSON 解析器解析亲澡。
通俗的來講就是:JSONP的最基本的原理是:動態(tài)添加一個標簽钦扭,而script標簽的src屬性是沒有跨域的限制的。這樣說來床绪,這種跨域方式其實與ajax XmlHttpRequest協(xié)議無關了客情。標簽,而script標簽的src屬性是沒有跨域的限制的癞己。這樣說來膀斋,這種跨域方式其實與ajax XmlHttpRequest協(xié)議無關了。
下面是簡單的dome? 由于訪問本地沒有跨域痹雅,這里只是講解:
紅色框內(nèi)是一個ajax的跨域請求仰担,假設前端請求的是一個不在當前域地址,這個時候瀏覽器會出現(xiàn)跨域的錯誤提示绩社;
于是我們用,<script>標簽的特性來解決這問題:
這樣的話摔蓝,就能正常訪問跨域的請求接口了。
兩者相比:
Jsonp不會因為瀏覽器原因而不支持跨域愉耙,但是jsonp只是支持get方式的請求贮尉,所以兩者相比的話,因環(huán)境而定朴沿,并沒有誰好誰劣