在 Web 應用程序中,Cookie-Session 是一種標準的身份驗證方法。餅乾,也被稱爲“sweet cookies”。類型爲“小文本文件”,是指一些網站爲了識別用戶身份而存儲在客戶端的數據。Session的主要功能是通過服務器記錄用戶的狀態。
在典型的在線購物場景中,用戶瀏覽多個頁面並將一些商品添加到購物車。儘管如此,系統並不知道是哪個用戶進行了操作,因爲HTTP協議是無狀態的,所以服務端需要爲不同的用戶創建相應的Session來識別和跟蹤這個用戶。在服務器端保存Session的方式有很多種,比如保存到內存、數據庫或者文件中。
我們看一下Cookie-Session的認證過程:
這是一個典型的 HTTP 客戶端(瀏覽器)和 HTTP 服務器對話,服務器運行在同一臺計算機(本地主機)上,包含以下步驟。
- 用戶訪問登錄頁面後,輸入相應的用戶名和密碼進行登錄操作。
- 在客戶端發起登錄請求。一般在瀏覽器環境下,可以通過AJAX或者Form發起登錄請求。
- 當服務器通過認證時,將爲認證成功的用戶創建一個Session,並存儲Session信息。之後服務器會通過HTTP響應頭來設置SessionId,即HTTP響應頭中會包含 的響應頭信息Set-Cookie。如果認證失敗,則提示用戶進行相關操作。
- 認證成功後,用戶訪問頁面獲取用戶信息,此時客戶端會在HTTP請求頭中攜帶cookie信息。
- 服務端接收到客戶端發起的請求,獲取cookie中存儲的SessionId來驗證用戶身份,驗證通過後返回相應信息。
下面我將使用Koa來介紹Cookie-Session的認證過程。首先我們來定義首頁的路由:
// router.js
路由器。get ( "/" , async ( ctx ) => {
if (ctx.session.user ) { const { userName } = ctx.session.user ; ctx.body = ` < h1 > Cookie /Session Authentication</h1> 你好, ${userName} <div> <a href="./getUserInfo">用戶信息</a> <a href="./logout">註銷</a> </div> ` ; } else { ctx.
“登錄”);
}
});
在上面的代碼中,我們首先會判斷當前用戶是否已經登錄到web應用中。如果已登錄,將顯示與當前用戶對應的問候消息。如果沒有登錄,會跳轉到登錄頁面,所以我們需要註冊登錄頁面的路由:
// router.js
路由器。get ( "/login" , ( ctx ) => {
if (ctx.session.user ) { ctx.redirect ( " /" ); } else { ctx.body = ` <form method = " post" action="/ login"> <p><label>用戶名</label><input type="text" name="username" /></p> <p><label>密碼</label><input type="password" name="密碼" /></p> <button type="
當未登錄的用戶在瀏覽器中訪問
http://localhost:3000/login地址時,會顯示如下用戶登錄表單:
當用戶輸入用戶名和密碼並點擊登錄按鈕時,瀏覽器會發起POST請求並調用/loginAPI。對應的/login路由處理邏輯如下:
// router.js
路由器。post ( "/login" , async (ctx) => {
try {
const loginData = ctx.request . body ; const
{ username, password } = loginData;
if (username === "bytefer" && password === "123 " ) {
ctx. session . user = { userName : "bytefer" , userId : "007" };
ctx. response . redirect ( "/"
}否則{
ctx。body = {代碼:0,消息:“登錄失敗!” };
}
} catch (err) {
throw new Error ( "登錄錯誤" );
}
});
在上面的代碼中,我們使用ctx.request.body獲取用戶輸入的用戶名和密碼,然後判斷輸入的用戶名和密碼是否完全匹配。當然,在真實的web項目中,通常會判斷輸入的信息與數據庫user表中的信息一致。此外,爲確保系統的安全,用戶的密碼將被加密或散列。如果輸入的用戶信息完全匹配,我們將當前登錄的用戶信息設置到該ctx.session.user屬性並重定向到首頁。並且如果用戶信息不匹配,頁面上會顯示相應的錯誤信息。
我們看一下登錄成功後服務器返回的HTTP響應報文:
從上圖可以看出,登錄成功後,服務器返回的HTTP響應報文中會包含Set-Cookie響應頭。之後我們打開Chrome開發者工具的Application Tab頁面,可以看到已經設置好的Cookie信息:
對於大部分的Web應用,一般都會提供一個用戶信息頁面,讓用戶可以查看當前已經登錄成功的用戶信息。這裏我們也註冊了一個/getUserInfo路由,用於顯示已登錄的用戶信息:
// router.js
路由器。get ( "/getUserInfo" , async ( ctx ) => {
if ( ctx.session.user ) { const { userName, userId } = ctx.session.user ; ctx.body = ` < div > UserName : ${userName} , UserId: ${userId} </div> ` ; } else { ctx.body = { code : 0 , message :
“您還沒有登錄。” };
}
});
用戶登錄成功後,訪問web應用中的其他路由時,會發起相應的HTTP請求,請求頭中會自動攜帶當前用戶的Cookie信息,如下圖:
除了上述功能,我們還需要註冊一個路由來處理用戶註銷操作:
// router.js
路由器。get ( "/logout" , async ( ctx ) => {
ctx.session = null ; ctx.body = ` < h1 >成功註銷</h1> <a href="./login">登錄</a> ` ; });
同樣我們看一下注銷成功後服務器返回的HTTP響應報文:
已經描述了包含在 Web 應用程序中的路由。爲了應用能夠正常運行,我們還需要創建一個Koa應用,並配置koa-session、koa-bodyparser等中間件。具體代碼如下:
// app.js
const Koa = require ( "koa" );
const app = new Koa ();
const session = require ( "koa-session" );
const bodyParser = require ( "koa-bodyparser" );
常量端口 = 3000 ;
const router = require ( "./router.js" );
const CONFIG = {
key : "sid" ,
maxAge : 86400000 ,
httpOnly: true ,
signed : true ,
rolling : false ,
renew : false ,
sameSite : null ,
};
應用程序。鍵= [ “bytefer” ];
應用程序。use ( async function ( ctx, next ) { try
{ await
next ( );
} catch (err) {
ctx.status = err.status || 500 ; ctx.type
= "html" ;
CTX。body = "<p>系統錯誤</p>" ;
CTX。應用程序。發出(“錯誤”,錯誤,ctx);
}
});
應用程序。使用(會話(配置,應用程序));
應用程序。使用( bodyParser ());
應用程序。使用(路由器。路由())。使用(路由器。allowedMethods());
應用程序。on ( "error" , ( err, ctx ) => {
控制檯. 日誌(錯誤);
});
應用程序。listen (port, function ( ) {
console . log ( `服務器運行在 http://localhost: ${port} ` );
});
Cookie-Session的認證過程已經介紹過了,最後總結一下這種認證方式存在的一些問題。
餅乾的缺陷
- 每次HTTP請求都會添加Cookies,無形中增加了流量。
- 安全性差,攻擊者可以利用本地 cookie 進行欺騙和 CSRF 攻擊。
- Cookie 大小限制在 4KB 左右,不足以滿足複雜的存儲要求。
會話缺陷
- session保存在服務器端,如果短時間內有大量用戶,會影響服務器的性能。
- 可擴展性不好。當有多臺服務器時,如何共享Session就會成爲一個問題。也就是說當用戶第一次訪問服務器A,第二次請求轉發給服務器B時,服務器B無從知曉其狀態。
那麼如何解決以上問題呢?解決方案之一是使用基於令牌的身份驗證。在下一篇文章中,我將介紹JWT認證方式。有興趣記得關注我哦。
如果需要更加全面的學好前端,也可以來參與我們的三十天學習計劃,全程不涉及任何費用!這是一套免費的三十天挑戰計劃的課程體系,包含了html+css+雲端部署的課程體系,可以通過釘釘羣裏學習,有問題在羣裏可以提問,同時每節課還安排有作業,配套有階段項目練習和綜合項目實戰,目的是幫助大家夯實前端基礎,輕鬆入門到前端行業
爲幫助到一部分同學不走彎路,真正達到一線互聯網大廠前端項目研發要求,首次實力寵粉,打造了《30天挑戰學習計劃》,內容如下:
HTML/HTML5,CSS/CSS3,JavaScript,真實企業項目開發,雲服務器部署上線,從入門到精通
- PC端項目開發(1個)
- 移動WebApp開發(2個)
- 多端響應式開發(1個)
共4大完整的項目開發 !一行一行代碼帶領實踐開發,實際企業開發怎麼做我們就是怎麼做。從學習一開始就進入工作狀態,省得浪費時間。
從學習一開始就同步使用 Git 進行項目代碼的版本的管理,Markdown 記錄學習筆記,包括真實大廠項目的開發標準和設計規範,命名規範,項目代碼規範,SEO優化規範
從藍湖UI設計稿 到 PC端,移動端,多端響應式開發項目開發
- 真機調試,雲服務部署上線;
- Linux環境下 的 Nginx 部署,Nginx 性能優化;
- Gzip 壓縮,HTTPS 加密協議,域名服務器備案,解析;
- 企業項目域名跳轉的終極解決方案,多網站、多系統部署;
- 使用 使用 Git 在線項目部署;
這些內容在《30天挑戰學習計劃》中每一個細節都有講到,包含視頻+圖文教程+項目資料素材等。只爲實力寵粉,真正一次掌握企業項目開發必備技能,不走彎路 !
過程中【不涉及】任何費用和利益,非誠勿擾 。
如果你沒有添加助理老師微信,可以添加下方微信,說明要參加30天挑戰學習計劃,來自公衆號!老師會邀請你進入學習,並給你發放相關資料。