Javascript實(shí)現(xiàn)跨域后臺設(shè)置攔截的方法詳解
本文主要給大家介紹了關(guān)于Javascript跨域后臺設(shè)置攔截的相關(guān)內(nèi)容,分享出來供大家參考學(xué)習(xí),話不多說了,來一起看看詳細(xì)的介紹吧。
子域名之間互相訪問需要跨域
結(jié)論放在開頭:
1.服務(wù)端必須設(shè)置允許跨域
2.客戶端帶cookie需要設(shè)置 withCredentials
3.無論服務(wù)端是否允許跨域,該request都會完整執(zhí)行
4. options 預(yù)請求需要設(shè)置返回空,不然requestMapping沒有支持該方法則出錯(cuò)
環(huán)境搭建
需求
首先需要搭建兩個(gè)環(huán)境。一個(gè)是提供API的server A,一個(gè)是需要跨域訪問API的server B。
Server A提供了一個(gè)api。完整的請求request是:
https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759&_=1490855801818
Server B有個(gè)頁面page:
http://cros.corstest.com.net:3001/test.html
并且這個(gè)page需要請求server A的api。
但由于跨域保護(hù),請求失?。?br />
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'xxxxx' is therefore not allowed access.
修改host
首先本地配置兩個(gè)指向127.0.0.1的host,方便互相跨域。
127.0.0.1 local.corstest.com.net 127.0.0.1 cros.corstest.com.net
啟動(dòng)項(xiàng)目A,方便提供API。
至于項(xiàng)目B,測試跨域只要寫個(gè)html靜態(tài)頁面即可。那么就寫一個(gè)test.html,并通過一個(gè)工具發(fā)布:
browser-sync
安裝
npm install -g browser-sync
本地啟動(dòng)一個(gè)test.html
browser-sync start --server --files "*.html" --host "cros.corstest.com.net" --port 3001
關(guān)于跨域CORS
ruanyifeng 的文章里說瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
其中同時(shí)滿足一下2種標(biāo)準(zhǔn)即為簡單跨域:
1) 請求方法是以下三種方法之一:
- HEAD
- GET
- POST
2)HTTP的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三個(gè)值application/x-www-form-urlencoded、multipart/form-data、text/plain
而其他情況,非簡單請求是那種對服務(wù)器有特殊要求的請求,比如請求方法是 PUT 或 DELETE ,或者 Content-Type 字段的類型是 application/json 。非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預(yù)檢"請求(preflight),即 options 請求。
關(guān)鍵
跨域的關(guān)鍵是瀏覽器獲得服務(wù)器的認(rèn)可,而服務(wù)器的認(rèn)可就是header里的 Access-Control-Allow-Origin 。瀏覽器通過比較服務(wù)端返回的response中是否包含這個(gè)字段,以及包含這個(gè)字段的內(nèi)容是否是當(dāng)前網(wǎng)址來確定是否跨域。也就是說繞過瀏覽器是可以不用跨域的。
有個(gè)問題,看好多文章并沒有指出。
第一點(diǎn),帶cookie問題。瀏覽器設(shè)置 withCredentials 為 true 則會帶cookie發(fā)送給服務(wù)端。而服務(wù)端設(shè)置 Access-Control-Allow-Credentials 為 true 則接收, false 則不接受。關(guān)鍵是到filter里的時(shí)候才會決定是否設(shè)置response,那么這時(shí)候cookie已經(jīng)存在request里了吧。(待驗(yàn)證)
驗(yàn)證:server端確實(shí)已經(jīng)接受了cookie,即使設(shè)置為false,服務(wù)端仍舊接受cookie。而客戶端也仍舊可以發(fā)送cookie。
第二點(diǎn),簡單跨域中,瀏覽器的請求直接發(fā)送給服務(wù)器,服務(wù)器返回是否支持跨域(即是否header加origin), 那么簡單跨域究竟是請求了服務(wù)端幾次?如果是1次,那么如果服務(wù)端不支持跨域,即沒有設(shè)置allow,還會不會繼續(xù)走下去,會不會繼續(xù)request得到結(jié)果后放入response?就是不論跨域不跨域服務(wù)器是否都會執(zhí)行這個(gè)request對應(yīng)的計(jì)算。因?yàn)樗械脑O(shè)置header都是給瀏覽器告知的,和服務(wù)端限制無關(guān)。(待驗(yàn)證)
驗(yàn)證:即使服務(wù)端沒有設(shè)置允許跨域,當(dāng)客戶端請求過來時(shí),服務(wù)端仍舊完整執(zhí)行了請求并返回,只是客戶端沒有接收。
服務(wù)端需要做點(diǎn)工作
針對上述兩種跨域。server A需要寫一個(gè)filter。
<filter> <filter-name>cors</filter-name> <filter-class>com.test.filter.CorsFilter</filter-class> </filter> <filter-mapping> <filter-name>cors</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </filter>
Filter:
public class CorsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
URL requestURL = new URL(request.getRequestURL().toString());
String hostName = requestURL.getHost();
String origin = request.getHeader("Origin");
int index = hostName.indexOf(".");
if(index > -1) {
String domainHost = hostName.substring(index, hostName.length());
if(!StringUtils.isEmpty(origin) && origin.contains(domainHost)) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Origin", origin);
response.addHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
response.addHeader("Access-Control-Allow-Headers", "Content-Type, Cookie, " +
"Accept-Encoding, User-Agent, " +
"Host, Referer, " +
"X-Requested-With, Accept, " +
"Accept-Language, Cache-Control, Connection");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
// CORS "pre-flight" request
response.setStatus(200);
return;
}
}
}
filterChain.doFilter(request, response);
}
}
上述filter是為了同一個(gè)domain下,不同子域名可以跨域訪問,而其他domain則不可以,因?yàn)槲覀冃枰蚕韈ookie,所以設(shè)置 Access-Control-Allow-Credentials 為 true . 如果設(shè)置為 false 則不接受cookie。
客戶端,即server B如果想要發(fā)送cookie則需要設(shè)置 withCredentials 為 true .
//原生
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
//jquery
$.ajax({
...
xhrFields: {
withCredentials: true
}
...
});
注意:針對非簡單跨域的時(shí)候發(fā)送 options 請求,服務(wù)端A需要告訴瀏覽器是否支持跨域即可,不要往下走了,不然到指定的requestMapping發(fā)現(xiàn)不支持這個(gè)方法就會很尷尬了,所以直接返回。
下面針對簡單跨域和非簡單跨域做測試:
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>test</title>
<script src="jquery-1.11.3.js"></script>
</head>
<body>
<input type="button" value="GET_Default" onclick="testGetDefault()">
<input type="button" value="GET_JSON" onclick="testGetJSON()">
<input type="button" value="POST_Default" onclick="testPostDefault()">
<input type="button" value="POST_JSON" onclick="testPostJson()">
<input type="button" value="PUT" onclick="testPUT()">
<script>
var getUrl = "https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759";
var postUrl = "https://local.corstest.com.net:8443/contentmain/saveReservationDeposits.json?htid=759";
function testGetDefault(){
sendAjax("GET",getUrl, "json", "application/x-www-form-urlencoded");
}
function testGetJSON(){
sendAjax("GET",getUrl, "json", "application/json; charset=utf-8");
}
function testPostDefault(){
sendAjax("POST",postUrl, "json", "application/x-www-form-urlencoded");
}
function testPostJson(){
sendAjax("POST",postUrl, "json", "application/json; charset=utf-8");
}
function testPUT(){
sendAjax("PUT",postUrl, "json", "application/json; charset=utf-8");
}
function sendAjax(type, url, dataType, contentType){
$.ajax( {
type: type,
url: url,
xhrFields: {
withCredentials: true
},
dataType : dataType, // accept type
contentType: contentType, //request type, default is application/x-www-form-urlencoded
success: function(result){
console.log(result);
},
error: function (xhr) {
console.log(xhr);
}
});
}
</script>
</body>
</html>
結(jié)果:
GET default:
只發(fā)送一個(gè)正常的get請求。
GET json:
先發(fā)送一個(gè)options如下:
General: Request URL:https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759 Request Method:OPTIONS Status Code:200 OK Remote Address:127.0.0.1:8443 Response Headers: Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, X-Requested-With, Accept, Accept-Language, Cache-Control, Connection Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Origin:http://cros.corstest.com.net:3001 Content-Length:0 Date:Thu, 30 Mar 2017 12:47:44 GMT Server:Apache-Coyote/1.1 Request Headers: Accept:*/* Accept-Encoding:gzip, deflate, sdch, br Accept-Language:zh-CN,zh;q=0.8 Access-Control-Request-Headers:content-type Access-Control-Request-Method:GET Connection:keep-alive Host:local.corstest.com.net:8443 Origin:http://cros.corstest.com.net:3001 Referer:http://cros.corstest.com.net:3001/test.html User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
然后再發(fā)送正常的Get請求。
post default:
正常發(fā)送請求。
post json: 先發(fā)送一個(gè)options請求。然后再發(fā)送正常的請求。
其他同理,總之,非簡單跨域會多發(fā)一次options請求來確認(rèn)是否支持跨域,這時(shí)候服務(wù)端一定要返回支持跨域,并且直接返回即可。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
js實(shí)現(xiàn)仿網(wǎng)易點(diǎn)擊彈出提示同時(shí)背景變暗效果
這篇文章主要介紹了js實(shí)現(xiàn)仿網(wǎng)易點(diǎn)擊彈出提示同時(shí)背景變暗效果,涉及javascript彈出框及頁面元素樣式操作的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08
如何實(shí)現(xiàn)一個(gè)webpack模塊解析器
這篇文章主要介紹了如何實(shí)現(xiàn)一個(gè)webpack模塊解析器,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10
javascript運(yùn)動(dòng)框架用法實(shí)例分析(實(shí)現(xiàn)放大與縮小效果)
這篇文章主要介紹了javascript運(yùn)動(dòng)框架用法,結(jié)合實(shí)例形式分析了javascript運(yùn)動(dòng)框架的實(shí)現(xiàn)與使用技巧,可實(shí)現(xiàn)div塊的放大與縮小功能,需要的朋友可以參考下2016-01-01
Javascript中匿名函數(shù)的調(diào)用與寫法實(shí)例詳解(多種)
js中定義函數(shù)的方式有很多種,函數(shù)直接量就是其中一種,下面通過本文給大家介紹匿名函數(shù)是如何調(diào)用的及匿名函數(shù)的n中寫法,對js匿名函數(shù)調(diào)用,js匿名函數(shù)寫法相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧2016-01-01
js 優(yōu)化次數(shù)過多的循環(huán) 考慮到性能問題
IE沒有我們想象中笨,它知道總的循環(huán)次數(shù)還是一千萬次。因此,得把這一百個(gè)十萬次循環(huán)分開執(zhí)行。雖然Javascript是單線程的,但也可以通過setTimeout或setInterval模擬多線程。2011-03-03
echarts折線圖月份數(shù)據(jù)不足自動(dòng)補(bǔ)0和日期達(dá)到數(shù)據(jù)連續(xù)的效果(最新推薦)
這篇文章主要介紹了echarts折線圖月份數(shù)據(jù)不足自動(dòng)補(bǔ)0和日期達(dá)到數(shù)據(jù)連續(xù)的效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-03-03

