天天看點

session登入認證方案--登陸狀态的服務端存儲(附上node原生開發代碼)

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是否存在,存在即使用者是登陸狀态的