1.為什么會有跨域席赂?
? ?? ?隨著軟件開發(fā)分工趨于精細(xì),前后端開發(fā)分離成為趨勢时迫,前端同事負(fù)責(zé)前端頁面的展示及頁面邏輯處理颅停,服務(wù)端同事負(fù)責(zé)業(yè)務(wù)邏輯處理同時通過API為前端提供數(shù)據(jù)也為前端提供數(shù)據(jù)的持久化能力,考慮到前后端同事開發(fā)工具和習(xí)慣的不同掠拳,必然需要將前后端項目進行獨立癞揉,再者考慮到網(wǎng)站訪問速度的問題,需要將靜態(tài)資源部署到CDN服務(wù)器上這樣項目分離也成為了必然溺欧。然而項目分離部署分離帶來的問題就是跨域請求的問題喊熟。
本例對比較流行的兩種跨域訪問方式(Jsonp和CORS)進行討論。(注:示例代碼附在文章末尾處)
1.JSONP
? ?? ?JSONP是利用瀏覽器對script的資源引用沒有同源限制姐刁,通過動態(tài)插入一個script標(biāo)簽芥牌,當(dāng)資源加載到頁面后會立即執(zhí)行的原理實現(xiàn)跨域的。JSONP是一種非正式傳輸協(xié)議龙填,該協(xié)議的一個要點就是允許用戶傳遞一個callback或者開始就定義一個回調(diào)方法胳泉,參數(shù)給服務(wù)端,然后服務(wù)端返回數(shù)據(jù)時會將這個callback參數(shù)作為函數(shù)名來包裹住JSON數(shù)據(jù)岩遗,這樣客戶端就可以隨意定制自己的函數(shù)來自動處理返回數(shù)據(jù)了扇商。
JSONP只支持GET請求而不支持POST等其它類型的HTTP請求,它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調(diào)用的問題宿礁,JSONP的優(yōu)勢在于支持老式瀏覽器案铺,弊端也比較明顯:需要客戶端和服務(wù)端定制進行開發(fā),服務(wù)端返回的數(shù)據(jù)不能是標(biāo)準(zhǔn)的Json數(shù)據(jù)梆靖,而是callback包裹的數(shù)據(jù)控汉。
2.CORS
CORS是現(xiàn)代瀏覽器支持跨域資源請求的一種方式,全稱是"跨域資源共享"(Cross-origin resource sharing)返吻,當(dāng)使用XMLHttpRequest發(fā)送請求時姑子,瀏覽器發(fā)現(xiàn)該請求不符合同源策略,會給該請求加一個請求頭:Origin测僵,后臺進行一系列處理街佑,如果確定接受請求則在返回結(jié)果中加入一個響應(yīng)頭:Access-Control-Allow-Origin;瀏覽器判斷該相應(yīng)頭中是否包含Origin的值谢翎,如果有則瀏覽器會處理響應(yīng),我們就可以拿到響應(yīng)數(shù)據(jù)沐旨,如果不包含瀏覽器直接駁回森逮,這時我們無法拿到響應(yīng)數(shù)據(jù)。
? ?? ?CORS與JSONP的使用目的相同磁携,但是比JSONP更強大褒侧,CORS支持所有的瀏覽器請求類型,承載的請求數(shù)據(jù)量更大谊迄,開放更簡潔闷供,服務(wù)端只需要將處理后的數(shù)據(jù)直接返回,不需要再特殊處理鳞上。
? ?? ?1.不需要操作cookie
? ?? ?? ?? ?? ? 只需要設(shè)置:setHeader("Access-Control-Allow-Origin","url/*")
? ?? ?2.如果需要操作cookie
? ?? ?? ?? ?? ? 前臺:js需要增加參數(shù):{'withCredentials':true}
? ?? ?? ?? ?? ? 后臺:
? ?? ?? ?? ?? ?? ?? ?? ?setHeader("Access-Control-Allow-Origin","url") //注意:地址不能使用"*"代表所有
? ?? ?? ?? ?? ?? ?? ?? ?setHeader("Access-Control-Allow-Credentials","true") //支持操作cookie
? ?? ???注解方式:spring4.2版本以后提供了對跨域注解的支持这吻,可以使用注解 @CorsOrigin
因需要實現(xiàn)跨域請求,跨域可以是:協(xié)議不一樣,域名不一樣,ip地址不一樣,端口號不一樣等多種情況篙议。為了簡單,下面示例代碼使用tomcat服務(wù)器怠硼,端口號不同鬼贱,分別部署兩個項目,項目之間進行數(shù)據(jù)交互香璃,驗證跨域請求.
2個項目这难,展示如下:
1.JSONP示例代碼:
1.1jsonp前端代碼:(注:代碼在demo1項目中)
function jsonpTest1() {//傳統(tǒng)支持jsonp的方案
? ? ? ? $.ajax({
? ? ? ? url: 'http://localhost:8081/jsonp_demo2/jsonp/test1.do',
? ? ? ? dataType: 'jsonp',
? ? ? ? success: function (res, status, xhr) {
? ? ? ? ? ? ? ? alert(status);
? ? ? ? ? ? ? ? alert(res.msg);
? ? ? ? }
? ? });
}
function jsonpTest2() {//使用MappingJacksonValue,從spring4.1以后版本才可以使用
? ? ? ? $.ajax({
? ? ? ? url: 'http://localhost:8081/jsonp_demo2/jsonp/test2.do',
? ? ? ? dataType: 'jsonp',
? ? ? ? success: function (res, status, xhr) {
? ? ? ? ? ? ? ? alert(status);
? ? ? ? ? ? ? ? var resJson = $.parseJSON(res);
? ? ? ? ? ? ? ? alert(resJson.msg);
? ? ? ? }
? ? });
}
1.2jsonp后臺代碼:(注:代碼在demo2項目中)
/**
* @author:admin
* 20182018年3月30日上午11:03:03
*/
/**
* 描述:
* @author zhuan
* @version 2018年3月30日 上午11:03:03
*/
package com.itcast.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.MediaType;
/**
* JS跨域解決方案一
* 描述:JSONP跨域請求controller
* @author zhuan
* @version 2018年3月31日 上午9:58:50
*/
@RestController
@RequestMapping("/jsonp")
public class JsonpController{
? ? ? ? /**
? ? ? ? *
? ? ? ? * 描述:傳統(tǒng)支持jsonp的方案
? ? ? ? * @param param
? ? ? ? * @param callback
? ? ? ? * @return
? ? ? ? */
? ? ? ? @RequestMapping(value = "/test1", produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
? ? ? ? public String jsonTest1(String param, String callback) {
? ? ? ? ? ? ? ? if (StringUtils.isNotBlank(callback)) {
? ? ? ? ? ? ? ? ? ? ? ? // 客戶端為jsonp請求葡秒。需要返回js代碼
? ? ? ? ? ? ? ? ? ? ? ? String jsonResutl = callback + "({\"type\":\"json\",\"msg\":\"test1 jsonp request success\"})";
? ? ? ? ? ? ? ? ? ? ? ? return jsonResutl;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return "{\"type\":\"json\",\"msg\":\"test1 json request success\"}";
? ? ? ? }
? ? ? ? /**
? ? ? ? *
? ? ? ? * 描述:從spring4.1以后版本才可以使用
? ? ? ? *
? ? ? ? * @param param
? ? ? ? * @param callback
? ? ? ? * @return
? ? ? ? */
? ? ? ? @RequestMapping("/test2")
? ? ? ? public Object jsonTest2(String param,String callback){
? ? ? ? ? ? ? ? if(StringUtils.isNotBlank(callback)){
? ? ? ? ? ? ? ? ? ? ? ? MappingJacksonValue mappingJacksonValue = new MappingJacksonValue("{\"type\":\"jsonp\",\"msg\":\"test2 jsonp request success\"}");
? ? ? ? ? ? ? ? ? ? ? ? //設(shè)置回調(diào)方法
? ? ? ? ? ? ? ? ? ? ? ? mappingJacksonValue.setJsonpFunction(callback);
? ? ? ? ? ? ? ? ? ? ? ? return mappingJacksonValue;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return "{\"type\":\"json\",\"msg\":\"test2 json request success\"}";
? ? ? ? }
}
2.CORS示例代碼:
2.1cors前端代碼:(注:代碼在demo1項目中)
function corsTest() {
? ? ? ? $.ajax({
? ? ? ? url: 'http://localhost:8081/jsonp_demo2/cors/test.do',
? ? ? ? dataType: 'json',
? ? ? ? success: function (res, status, xhr) {
? ? ? ? ? ? ? ? alert(status);
? ? ? ? ? ? ? ? alert(res.msg);
? ? ? ? }
? ? });
}
2.2cors后臺代碼:(注:代碼在demo2項目中)
package com.itcast.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* JS跨域解決方案二 描述:CORS跨域請求controller
*
* @author zhuan
* @version 2018年3月31日 上午9:58:50
*/
@RestController
@RequestMapping("/cors")
public class CorsController {
? ? ? ? // 1.不支持操作cookie
? ? ? ? // 只需要設(shè)置:setHeader("Access-Control-Allow-Origin","url/*")
? ? ? ? // 2.支持操作cookie
? ? ? ? // 前臺:
? ? ? ? // js需要增加參數(shù):{'withCredentials':true}
? ? ? ? // 后臺:
? ? ? ? // setHeader("Access-Control-Allow-Origin","url") //注意:地址不能使用"*"代表所有
? ? ? ? // setHeader("Access-Control-Allow-Credentials","true") //支持操作cookie
? ? ? ? // 注解方式:spring4.2版本以后提供了對跨域注解的支持 @CorsOrigin
? ? ? ? /**
? ? ? ? *
? ? ? ? * 描述:
? ? ? ? *
? ? ? ? * @param param
? ? ? ? * @param callback
? ? ? ? * @return
? ? ? ? */
? ? ? ? @RequestMapping("/test")
? ? ? ? @CrossOrigin(origins = "http://localhost:8080")
? ? ? ? public String corsTest1(String param) {
? ? ? ? ? ? ? ? return "{\"type\":\"json\",\"msg\":\"cors json request success\"}";
? ? ? ? }
}