同源策略SOP(Same origin policy)是一種約定(即"協(xié)議+域名+端口"三者相同)概龄,它是瀏覽器最核心也最基本的安全功能右蹦,如果缺少了同源策略厕诡,瀏覽器很容易受到攻擊烟很。協(xié)議谴垫,域名竖伯,端口一種不同都是不同源,即跨域了
跨域會(huì)有如下限制:
1.Cookie存哲、LocalStorage 和 IndexDB 無(wú)法讀取
2.DOM 和 Js對(duì)象無(wú)法獲得
- AJAX 請(qǐng)求不能發(fā)送
-
解決跨域的方法:
一、通過(guò)jsonp跨域
1.原生實(shí)現(xiàn)
js七婴、css虏缸,img等靜態(tài)資源不受跨域的限制遍膜,可以通過(guò)動(dòng)態(tài)創(chuàng)建script,來(lái)解決跨域問(wèn)題.
下面這個(gè)html頁(yè)面和需要訪問(wèn)的php文件在同一協(xié)議,同一域名钠怯,不同端口下虱岂,所以是跨域請(qǐng)求
- index.html
<body>
<a>某個(gè)頁(yè)面</a><input type="button" value="想通過(guò)ajax去請(qǐng)求數(shù)據(jù)">
<script>
function getInfo(obj){
//得到數(shù)據(jù) 解析到頁(yè)面
console.log(obj);
//解析數(shù)據(jù)
}
document.querySelector("input").onclick = function(){
//XMLHttpRequest 去請(qǐng)求會(huì)有跨域限制,所以下面注釋的這種方法不能使用
// var xhr = new XMLHttpRequest();
// xhr.open("get","http://localhost/getData.php");
// xhr.send(null);
// xhr.onreadystatechange = function(){
// if(xhr.readyState === 4 && xhr.status === 200){
// var data = xhr.responseText;
// console.log(data);
// }
// }
var script = document.createElement("script");
script.src = "http://localhost/getData.php?callback=getInfo";//傳參一個(gè)回調(diào)函數(shù)名給后端屯伞,攜帶后端數(shù)據(jù)再執(zhí)行前端的這個(gè)回調(diào)函數(shù)
document.body.appendChild(script); //讓這個(gè)標(biāo)簽去發(fā)送請(qǐng)求 必須把這個(gè)標(biāo)簽掛載到頁(yè)面上
}
</script>
</body>
- getData.php
<?php
$Info = $_GET['callback']; //getInfo函數(shù)
//向客戶端輸出 getInfo("GetData");
//組拼一個(gè)getInfo("GetData")
echo $Info."("."'GetData'".")";
?>
2. jquery ajax
$.ajax({
url: 'http://localhost/getData.php',
type: 'get',
dataType: 'jsonp', // 請(qǐng)求方式為jsonp
jsonpCallback: "getInfo", // 自定義回調(diào)函數(shù)名
data: {}
});
jsonp缺點(diǎn):
1、只能實(shí)現(xiàn)get一種請(qǐng)求:
??因?yàn)閐ocument.createElement('script') 生成一個(gè) script 標(biāo)簽莽鸭,然后插 body 里吗伤。在這里根本沒(méi)有設(shè)置請(qǐng)求格式的余地。(script標(biāo)簽只能進(jìn)行g(shù)et請(qǐng)求)
2硫眨、只支持跨域 HTTP 請(qǐng)求這種情況足淆,不能解決不同域的兩個(gè)頁(yè)面之間如何進(jìn)行 JavaScript 調(diào)用的問(wèn)題
3、調(diào)用失敗的時(shí)候不會(huì)返回各種 HTTP 狀態(tài)碼。
4巧号、安全性族奢,萬(wàn)一假如提供 JSONP 的服務(wù)存在頁(yè)面注入漏洞,即它返回的 javascript 的內(nèi)容被人控制的
二丹鸿、 跨域資源共享(CORS)
普通跨域請(qǐng)求:服務(wù)端只需要設(shè)置Access-Control-Allow-Origin
即可越走,前端無(wú)須設(shè)置,若要帶cookie請(qǐng)求:前后端都需要設(shè)置后端需要設(shè)置 Access-Control-Allow-Credentials
為true靠欢,前端需要設(shè)置withCredentials
為true廊敌。
需注意的是:由于同源策略的限制,所讀取的cookie為跨域請(qǐng)求接口所在域的cookie掺涛,而非當(dāng)前頁(yè)。
在 cors 中會(huì)有簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求的概念
-
簡(jiǎn)單請(qǐng)求:不會(huì)出發(fā)cors預(yù)檢請(qǐng)求
1.請(qǐng)求方法是以下三種方法之一: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
凡是不同時(shí)滿足上面兩個(gè)條件,就屬于非簡(jiǎn)單請(qǐng)求伞广。
cors的工作原理:
1拣帽、瀏覽器先判斷是簡(jiǎn)單請(qǐng)求還是復(fù)雜請(qǐng)求。
2嚼锄、如果是復(fù)雜請(qǐng)求减拭,在正式請(qǐng)求前瀏覽器會(huì)用OPTIONS
方法先發(fā)送一個(gè)預(yù)檢請(qǐng)求,OPTIONS
是HTTP/1.1
協(xié)議中定義的方法区丑。
- 該方法不會(huì)對(duì)服務(wù)器資源產(chǎn)生影響拧粪,預(yù)檢請(qǐng)求中同時(shí)攜帶下面的首部字段:
Access-Control-Request-Method
: 這個(gè)字段表明了請(qǐng)求的方法;
Access-Control-Request-Headers
: 這個(gè)字段表明了這個(gè)請(qǐng)求的 Headers沧侥;
Origin
: 這個(gè)字段表明了請(qǐng)求發(fā)出的域可霎。 - 服務(wù)器接收到請(qǐng)求后允許攜帶的信息類型:
Access-Control-Allow-Origin
: 能夠被允許發(fā)出這個(gè)請(qǐng)求的域名,也可以使用*來(lái)表明允許所有域名宴杀;
Access-Control-Allow-Methods
: 用逗號(hào)分隔的被允許的請(qǐng)求方法的列表癣朗;
Access-Control-Allow-Headers
: 用逗號(hào)分隔的被允許的請(qǐng)求頭部字段的列表;
Access-Control-Max-Age
: 這個(gè)預(yù)檢請(qǐng)求能被緩存的最長(zhǎng)時(shí)間旺罢,在緩存時(shí)間內(nèi)旷余,同一個(gè)請(qǐng)求不會(huì)再次發(fā)出預(yù)檢請(qǐng)求。
3扁达、如果是簡(jiǎn)單請(qǐng)求正卧,瀏覽器直接發(fā)出cors請(qǐng)求。會(huì)在頭部信息自動(dòng)的添加origin字段跪解,來(lái)說(shuō)明子來(lái)自哪個(gè)源穗酥。服務(wù)器拿到請(qǐng)求之后,在回應(yīng)時(shí)對(duì)應(yīng)地添加Access-Control-Allow-Origin
字段,如果 Origin 不在這個(gè)字段的范圍中砾跃,那么瀏覽器就會(huì)將響應(yīng)攔截骏啰。
1.前端設(shè)置
(1)原生ajax
// 前端設(shè)置是否帶cookie
xhr.withCredentials = true;
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端設(shè)置是否帶cookie
xhr.withCredentials = true;
xhr.open('post', 'http://localhost/index.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
(2)jquery ajax
$.ajax({
...
xhr: {
withCredentials: true // 前端設(shè)置是否帶cookie
},
crossDomain: true, // 會(huì)讓請(qǐng)求頭中包含跨域的額外信息,但不會(huì)含cookie
...
});
2.后臺(tái)設(shè)置
? 若后端設(shè)置成功抽高,前端瀏覽器控制臺(tái)則不會(huì)出現(xiàn)跨域報(bào)錯(cuò)信息判耕,反之,說(shuō)明沒(méi)設(shè)成功翘骂。
(1)php
//在php中設(shè)置客跨域訪問(wèn)
if (config('app.environment') == 'local') { // 如果是本地環(huán)境就允許跨域訪問(wèn)
header('Access-Control-Allow-Origin: *');
//上面第一行說(shuō)到的Access-Control-Allow-Origin有多種設(shè)置方法:
//(1)設(shè)置*是最簡(jiǎn)單粗暴的壁熄,但是服務(wù)器出于安全考慮,肯定不會(huì)這么干碳竟,而且草丧,如果是*的話,瀏覽器將不會(huì)發(fā)送cookies莹桅,即使你的XHR設(shè)置了withCredentials
//(2) 指定域昌执,一般的系統(tǒng)中間都有一個(gè)nginx,所以推薦這種,例如:header("Access-Control-Allow-Origin","http://localhost:3000");
//(3)動(dòng)態(tài)設(shè)置為請(qǐng)求域诈泼,多人協(xié)作時(shí)懂拾,多個(gè)前端對(duì)接一個(gè)后臺(tái),這樣很方便
//withCredentials:表示XHR是否接收cookies和發(fā)送cookies铐达,也就是說(shuō)如果該值是false岖赋,響應(yīng)頭的Set-Cookie,瀏覽器也不會(huì)理瓮孙,并且即使有目標(biāo)站點(diǎn)的cookies唐断,瀏覽器也不會(huì)發(fā)送。
header('Access-Control-Allow-Credentials: true'); //是否允許后續(xù)請(qǐng)求攜帶認(rèn)證信息(cookies),該值只能是true,否則不返回
//預(yù)檢請(qǐng)求(參考文章:http://www.php.cn/div-tutorial-378889.html) --- 一般不用設(shè)置
//與簡(jiǎn)單請(qǐng)求不同的是杭抠,option請(qǐng)求多了2個(gè)字段:
//Access-Control-Request-Method:該次請(qǐng)求的請(qǐng)求方式
//Access-Control-Request-Headers:該次請(qǐng)求的自定義請(qǐng)求頭字段
//Access-Control-Max-Age 表明該響應(yīng)的有效時(shí)間為 86400 秒栗涂,也就是 24 小時(shí)。在有效時(shí)間內(nèi)祈争,瀏覽器無(wú)須為同一請(qǐng)求再次發(fā)起預(yù)檢請(qǐng)求斤程。請(qǐng)注意,瀏覽器自身維護(hù)了一個(gè)最大有效時(shí)間菩混,如果該首部字段的值超過(guò)了最大有效時(shí)間忿墅,將不會(huì)生效
//預(yù)檢結(jié)果緩存時(shí)間,也就是上面說(shuō)到的緩存啦
//'Access-Control-Max-Age: 86400'
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); //允許的請(qǐng)求類型
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept"); // 允許的請(qǐng)求頭字段
}
if (Request::isOptions()) { // 判斷是否為OPTIONS請(qǐng)求
exit; //因?yàn)轭A(yù)檢請(qǐng)求第一次是發(fā)送OPTIONS請(qǐng)求返回了響應(yīng)頭的內(nèi)容,但沒(méi)有返回響應(yīng)實(shí)體response body內(nèi)容沮峡。這個(gè)我們不處理業(yè)務(wù)邏輯疚脐,第二次接收的get或post等才是實(shí)質(zhì)的請(qǐng)求返回我們才處理
}
}
三、nginx反向代理
nginx是一種高性能的反向代理服務(wù)器
nginx拿到客戶端的請(qǐng)求邢疙,將請(qǐng)求轉(zhuǎn)發(fā)給其他的服務(wù)器棍弄,處理完成后返回給nginx望薄,nginx服務(wù)器會(huì)交給客戶端,主要的場(chǎng)景是維持服務(wù)器集群的負(fù)載均衡呼畸。
server {
listen 80;
server_name local.test;
location /api {
proxy_pass http://localhost:8080;
}
}
當(dāng)客戶訪問(wèn)local.test/api的時(shí)候痕支,nginx會(huì)將服務(wù)轉(zhuǎn)發(fā)到http://localhost:8080上,當(dāng)相應(yīng)返回后蛮原,將相應(yīng)內(nèi)容發(fā)給客戶端
四卧须、webpack中的proxy代理
proxy: { //代理
'/api': {
target: 'http://localhost:3000',
},
}
當(dāng)訪問(wèn)/api/xxx的時(shí)候,會(huì)轉(zhuǎn)向訪問(wèn)http://localhost:3000/xxx