1.原理:
session登入認證方案:使用者從用戶端傳遞使用者名、密碼等資訊,服務端認證後将資訊存儲在session中,将session_id放到cookie中。
以後通路其他頁面,自動從cookie中取到session_id,再從session中取認證資訊。相較于用戶端cookie登陸認證方案,服務端存儲登陸狀态的話,可以提升資料的安全性, 也能夠存儲更多的内容.
2.如何通過Session來存儲登入狀态
2.1給每一個使用者配置設定一個無關緊要的值session_id作為為一個辨別放到cookie中
2.2在服務端定義一個全局變量作為Session容器
2.3将使用者的唯一辨別作為key, 使用者登入之後就給容器的這個key添加登入狀态資訊
這樣使用者的資訊就全部存儲在服務端而不是用戶端了;
最關鍵的一點:
因為http是無狀态,req.session其實每次都是空的,但是我們有維護一個全局變量(使用者資訊表),那麼我們就可以根據cookie中的sessionid找到這個全局變量裡 的使用者認證資訊,指派給req.session,之後就可以通過req.session.username是否存在來判斷使用者是否在登陸狀态;
考慮特殊情況:
(1)用戶端手動删除了cookie,那麼服務端擷取不到sessionId了,就重新下發新的随機數作為sessionId,并在全局變量(使用者資訊表)中初始化sessionId的value為空對象,這樣用戶端是未登入狀态的,符合邏輯;
(2)服務端崩潰重新開機了,那麼服務端全局變量裡的内容全丢了,使用者資訊表為空,即便使用者帶着之前給的sessionId過來,但是我們在使用者資訊表裡沒有這個sessionId屬性,是以就初始化使用者資訊表裡的sessionId對應的value為空對象;
代碼1:initCookieSession函數
對所有的請求,處理的第一步就是準備cookie和session,就是調用initCookieSession函數,去把全局變量裡的使用者資訊指派給req.session,(如果沒登陸,req.session={},如果登陸過,req.session.username=xxx等使用者資訊是有值的),第二步才是處理靜态資源,傳回靜态網頁,第三步才是傳回接口api的資料
// 0.準備cookie和session
initCookieSession(req, res);
//隻要有請求發過來,都去檢查下有沒有我們下發的cookie,沒有的話,我們就下發一個cookie,cookie作為key存入session中,不過session中對應的value隻有登陸成功後才
//有的;如果有我們下發的cookie,那就什麼都不做,除非換人登陸成功了,session中key對應的value值才去改變;
//req.userId的作用:看有沒有我們下發的唯一辨別
//SESSION_CONTAINER的作用:session使用者資訊表,key是cookie,value是具體的使用者資訊
//req.session便于後續登陸成功時,存儲使用者具體資訊,它是等于SESSION_CONTAINER[req.userId];的
const initCookieSession = (req, res) =>{
// 1.處理Cookie,這一步隻是去分析cookie,看有沒有我們下發的唯一辨別
req.cookie = {};
if(req.headers.cookie){
req.headers.cookie.split(';').forEach((item)=>{
let keyValue = item.split('=');
req.cookie[keyValue[0]] = keyValue[1];
});
}
// 2.擷取使用者的唯一辨別
req.userId = req.cookie.userId;
if(!req.userId){
req.userId = `${Date.now()}_${Math.random()}_it666`;
// 給目前使用者配置設定容器
SESSION_CONTAINER[req.userId] = {};
res.setHeader('Set-Cookie',`userId=${req.userId}; path=/; httpOnly; expires=${getCookieExpires()}`);
}
//如果是空的,也要初始化為空對象,什麼情況下是空的?比如重新開機了node的情況下,req.userId是存在的,但是全局變量都沒有了,是以存在即便有cookie,也要配置設定容器的情況;
if(!SESSION_CONTAINER[req.userId]){
// 給目前使用者配置設定容器
SESSION_CONTAINER[req.userId] = {};
}
//隻有登陸之後,req.session裡才有這個使用者的資訊,在req.session裡存的東西,就是存到了SESSION_CONTAINER中的東西
//如果之前登陸了,那麼SESSION_CONTAINER[req.userId]是有認證資訊的,那麼
//就存儲在req.session中,後續處理的時候就能通過req.session.xx擷取已登陸使用者的資訊
req.session = SESSION_CONTAINER[req.userId];
console.log(req.session);
}
// 定義變量作為session的容器
const SESSION_CONTAINER = {};
/**
* 生成Cookie過期時間
* @returns {*}
*/
const getCookieExpires = () =>{
let date = new Date();
date.setTime(date.getTime() + (24 * 60 * 60 * 1000));
return date.toGMTString();
}
代碼2:使用者登陸時,去存儲使用者資訊到全局變量中
關鍵點:為什麼存儲到req.session中,也便存儲到了SESSION_CONTAINER中呢,因為處理請求的第一步就是調用initCookieSession函數,函數中
req.session = SESSION_CONTAINER[req.userId];這句代碼,SESSION_CONTAINER[req.userId]是對象,對象的指派是傳址,
是以往req.session裡存使用者資訊, 就是往全局變量裡存使用者資訊;
if(result.code === 200){
//登陸成功後,把使用者資訊存入req.session裡
req.session.username = result.data.username;
req.session.password = result.data.password;
req.session.gender = result.data.gender;
}
用戶端如何判斷自身是否在登陸狀态?
需要後端提供/isLogin的接口,用戶端請求接口方知
服務端如何判斷使用者是否在登陸狀态?
在調用了initCookieSession函數後,檢視req.session.username是否存在,存在即使用者是登陸狀态的