基礎(chǔ)知識(shí)
1.跨域
當(dāng)一個(gè)請(qǐng)求url的協(xié)議、域名彻舰、端口三者之間任意一個(gè)與當(dāng)前頁(yè)面url不同即為跨域 。
2. cookie與session
?在網(wǎng)站中刃唤,http請(qǐng)求是無(wú)狀態(tài)的隔心。 也就是說(shuō),你在同一客戶端上多次請(qǐng)求服務(wù)器尚胞,每次的請(qǐng)求都是沒(méi)有任何影響的,服務(wù)器并不會(huì)保留任何狀態(tài)信息笼裳。這就會(huì)帶來(lái)一個(gè)問(wèn)題躬柬,當(dāng)你輸入帳號(hào)密碼后,向服務(wù)器發(fā)送請(qǐng)求,經(jīng)服務(wù)器判斷你賬號(hào)密碼正確(數(shù)據(jù)庫(kù)中存在)返回你登錄成功的信息后堪嫂,你再次向服務(wù)器請(qǐng)求數(shù)據(jù)偎箫,服務(wù)器是并不知道你已經(jīng)登錄成功的狀態(tài)的,所以你還要重新登錄(當(dāng)然恶复,重新登錄也是沒(méi)有用的)怜森,這就陷入了死循環(huán)。
? cookie和session都是為了解決上述問(wèn)題的谤牡。
? cookie是通過(guò)客戶端保留狀態(tài)信息副硅。第一次登錄后服務(wù)器返回一些數(shù)據(jù)(cookie)給瀏覽器,然后瀏覽器保存在本地翅萤,當(dāng)該用戶發(fā)送第二次請(qǐng)求的時(shí)候恐疲,就會(huì)自動(dòng)的把上次請(qǐng)求存儲(chǔ)的cookie數(shù)據(jù)自動(dòng)的攜帶給服務(wù)器,服務(wù)器通過(guò)瀏覽器攜帶的數(shù)據(jù)就能判斷當(dāng)前用戶是哪個(gè)了套么。 然而由于cookie將狀態(tài)保存到了客戶端培己,這就很不安全。比如說(shuō)我不是一個(gè)網(wǎng)站的vip用戶胚泌,但是省咨,我可以通過(guò)檢查vip用戶向服務(wù)器發(fā)送的cookie偽造出來(lái)一個(gè)假的cookie,使自己獲得vip用戶的數(shù)據(jù)玷室。所以cookie里面不能保存比較敏感的信息零蓉。而且cookie存儲(chǔ)的數(shù)據(jù)量有限,不同的瀏覽器有不同的存儲(chǔ)大小阵苇,但一般不超過(guò)4KB。因此使用cookie只能存儲(chǔ)一些小量的數(shù)據(jù)感论。
? 而session就是將狀態(tài)保存到服務(wù)器上的绅项。客戶端瀏覽器訪問(wèn)服務(wù)器的時(shí)候比肄,服務(wù)器把客戶端信息以某種形式記錄在服務(wù)器上 快耿, 客戶端瀏覽器再次訪問(wèn)時(shí),只需要從該 Session 中查找該客戶的狀態(tài)就可以了 芳绩。
? 目前掀亥,狀態(tài)判斷的一種很流行方式是cookie與session結(jié)合實(shí)現(xiàn)的。還拿上面的登錄來(lái)舉例子妥色。當(dāng)你輸入帳號(hào)密碼后搪花,向服務(wù)器發(fā)送請(qǐng)求,服務(wù)器判斷你登錄成功后,會(huì)將你的登錄狀態(tài)記錄到session中撮竿,并且給瀏覽器返回一個(gè)session_id吮便,瀏覽器在cookie中保存這個(gè)session_id,在下一次向服務(wù)器請(qǐng)求數(shù)據(jù)的時(shí)候幢踏,會(huì)通過(guò)cookie將session_id傳給服務(wù)器髓需,服務(wù)器通過(guò)session_id找到對(duì)應(yīng)的session就可以判斷你登錄的狀態(tài)了。
3. express框架下的session實(shí)現(xiàn)
//引入express-session和cookie-parser包(npm下載)
const session = require("express-session");
const cookieParser = require("cookie-parser")
//配置session
app.use(session({
secret: "wisompark", //設(shè)置簽名秘鑰 內(nèi)容可以任意填寫(xiě)
cookie: {
maxAge: 60 * 1000 * 60
}, //設(shè)置cookie的過(guò)期時(shí)間房蝉,60分鐘
resave: true,
saveUninitialized: false
}))
//在調(diào)用登錄接口成功后設(shè)置session的值
req.session.login=true;
req.session.name=USERNAME;
//在調(diào)用數(shù)據(jù)的接口時(shí)僚匆,可以先對(duì)session進(jìn)行判斷再判斷進(jìn)行什么操作
resave 屬性為true時(shí):每次請(qǐng)求,無(wú)論session的內(nèi)容是否發(fā)生了變化搭幻,都要重新保存一下咧擂,如果要在服務(wù)器端要對(duì)session進(jìn)行操作的話,這個(gè)屬性必須設(shè)置為true粗卜。
saveUninitialized :是否設(shè)置session在存儲(chǔ)容器中可以給修改屋确。比如session過(guò)期30分鐘,沒(méi)有人操作時(shí)候session 30分鐘后過(guò)期续扔,如果有人操作攻臀,每次以當(dāng)前時(shí)間為起點(diǎn),使用原設(shè)定的maxAge重設(shè)session過(guò)期時(shí)間到30分鐘只有這種業(yè)務(wù)場(chǎng)景必須同行設(shè)置resave和rolling為true.同時(shí)saveUninitialized要設(shè)置為false允許修改纱昧。
問(wèn)題
問(wèn)題描述
? 設(shè)置完session配置后刨啸,登錄成功跳轉(zhuǎn)到管理頁(yè)面時(shí),還是要重新登錄识脆。在服務(wù)器端打印session發(fā)現(xiàn)第二次請(qǐng)求時(shí)的session中并沒(méi)有登錄成功后在session中設(shè)置的狀態(tài)值设联。
? 問(wèn)題出現(xiàn)的環(huán)境:node express框架打架后端,前端通過(guò)AJAX請(qǐng)求數(shù)據(jù)灼捂。
問(wèn)題分析
? 出現(xiàn)該問(wèn)題的主要原因是在跨域請(qǐng)求數(shù)據(jù)的時(shí)候离例,客戶端在向服務(wù)器請(qǐng)求數(shù)據(jù)的時(shí)候,所攜帶的cookie在服務(wù)器端是無(wú)法讀取的悉稠。所以在第二次請(qǐng)求數(shù)據(jù)時(shí)宫蛆,服務(wù)器并沒(méi)有接收到客戶端傳過(guò)來(lái)的cookie中的session_id,會(huì)認(rèn)為這個(gè)請(qǐng)求是無(wú)狀態(tài)的的猛,就會(huì)給他附上一個(gè)新的session耀盗,并向客戶端傳過(guò)去一個(gè)新的session_id。
解決問(wèn)題
思路一:既然是跨域造成的卦尊,就不讓其跨域就好了叛拷。
1.通過(guò)設(shè)置取消瀏覽器的同源策略;
2.將網(wǎng)絡(luò)服務(wù)器和后端數(shù)據(jù)服務(wù)器放在一起岂却;
在node中創(chuàng)建一個(gè)靜態(tài)目錄忿薇,將前端頁(yè)面放到靜態(tài)目錄中裙椭,這樣在請(qǐng)求數(shù)據(jù)的時(shí)候,協(xié)議煌恢、域名骇陈、端口都是一致的,就不會(huì)有跨域的問(wèn)題了瑰抵。
思路二:跨域時(shí)讓服務(wù)器端能讀取客戶端發(fā)過(guò)來(lái)的cookie
//前端設(shè)置
//jQuery
$.ajaxSetup({
xhrFields:{withCredentials: true
}
});
//axios
axios({
method: 'get',
url: 'XXX',
withCredentials: true
})
//后端設(shè)置
/*
設(shè)置Access-Control-Allow-Credentials為true
注意你雌,在設(shè)置了上面的參數(shù)后,Access-Control-Allow-Origin的值就不能為*了二汛。
*/
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:8848');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');//設(shè)置方法
res.header('Access-Control-Allow-Credentials', true);
if (req.method == 'OPTIONS') {
res.send(200); // 意思是婿崭,在正常的請(qǐng)求之前,會(huì)發(fā)送一個(gè)驗(yàn)證肴颊,是否可以請(qǐng)求氓栈。
}
else {
next();
}
});