摘要:基于以上原因,很多管理都是基于實(shí)現(xiàn)的。在經(jīng)過(guò)中間件的時(shí)候就會(huì)自動(dòng)完成的有效性驗(yàn)證延期重新頒發(fā)以及對(duì)中數(shù)據(jù)的獲取了。上述代碼只是對(duì)于請(qǐng)求的靜態(tài)處理,整個(gè)用戶(hù)管理的另一個(gè)方面則是狀態(tài)的切換用戶(hù)的登陸登出以及用戶(hù)數(shù)據(jù)的獲取。
基礎(chǔ)概念
Session管理是Web Application的基礎(chǔ)也是一個(gè)老生常談的話題。為了方便后文的展開(kāi),更重要的是確認(rèn)自己清晰的理解了整個(gè)Session管理的概念,我在此還是決定贅述的整個(gè)流程。如果你已經(jīng)對(duì)于Session概念非常清晰的話,可以跳過(guò)本節(jié)不影響對(duì)于后文的理解。
HTTP協(xié)議在設(shè)計(jì)的時(shí)候是無(wú)狀態(tài)的。這是一個(gè)很關(guān)鍵的概念,意味著服務(wù)器在處理請(qǐng)求的時(shí)候,并不關(guān)注這個(gè)請(qǐng)求是誰(shuí)發(fā)來(lái)的。這對(duì)于以提供內(nèi)容為核心的Web1.0,例如門(mén)戶(hù)網(wǎng)站,非常適合。然而對(duì)于以應(yīng)用服務(wù)為核心的Web 2.0而言,服務(wù)器端必須有能力從請(qǐng)求中提取出請(qǐng)求者的身份信息,以在請(qǐng)求者不用反復(fù)輸入身份的情況下,提供連續(xù)的服務(wù)。
實(shí)現(xiàn)請(qǐng)求身份驗(yàn)證的方式很多,其中一種廣泛接受的方式是使用服務(wù)器端產(chǎn)生的Session ID結(jié)合瀏覽器的Cookie實(shí)現(xiàn)對(duì)Session的管理,一般來(lái)說(shuō)包括以下4個(gè)步驟:
服務(wù)器端的產(chǎn)生Session ID
服務(wù)器端和客戶(hù)端存儲(chǔ)Session ID
從HTTP Header中提取Session ID
根據(jù)Session ID從服務(wù)器端的Hash中獲取請(qǐng)求者身份信息
上圖是一個(gè)使用Redis Cluster來(lái)實(shí)現(xiàn)對(duì)Session管理的流程,但本質(zhì)上除了redisMatrix.get和redisMatrix.set以外,和一般的Session管理流程是一致的。
簡(jiǎn)單來(lái)說(shuō),一個(gè)請(qǐng)求到達(dá)的時(shí)候,服務(wù)器會(huì)先判斷是否帶有Session信息。如果有,則根據(jù)Session ID去數(shù)據(jù)庫(kù)中查找是否具有對(duì)應(yīng)的用戶(hù)身份信息。此處可能會(huì)出現(xiàn)Session失效、非法的Session信息等可能性,那么服務(wù)器視同無(wú)Ssession信息的情況,重新的產(chǎn)生一個(gè)隨機(jī)的字符串,并且在Http返回頭中寫(xiě)入新的Session ID信息。另一者,如果服務(wù)器成功獲取了用戶(hù)的身份信息則以該身份為請(qǐng)求者提供服務(wù)。
使用Express和Redis對(duì)Session管理的實(shí)現(xiàn)Redis是一個(gè)非常適合用于Session管理的數(shù)據(jù)庫(kù)。第一,它的結(jié)構(gòu)簡(jiǎn)單,key-value的形式非常符合SessionID-UserID的存儲(chǔ);第二,讀寫(xiě)速度非???;第三,自身支持?jǐn)?shù)據(jù)自動(dòng)過(guò)期和清除;第四,語(yǔ)法、部署非常簡(jiǎn)單?;谝陨显颍芏郤ession管理都是基于Redis實(shí)現(xiàn)的。
Express已經(jīng)將Session管理的整個(gè)實(shí)現(xiàn)過(guò)程簡(jiǎn)化到僅僅幾行代碼的配置的地步了,你完全不用理解整個(gè)session產(chǎn)生、存儲(chǔ)、返回、過(guò)期、再頒發(fā)的結(jié)構(gòu),使用Express和Redis實(shí)現(xiàn)Session管理,只要兩個(gè)中間件就足夠了:
express-session
connect-redis
廢話不多說(shuō)還是上代碼:
var express = require("express"); var session = require("express-session"); var RedisStore = require("connect-redis")(session); var app = express(); var options = { "host": "127.0.0.1", "port": "6379", "ttl": 60 * 60 * 24 * 30, //Session的有效期為30天 }; // 此時(shí)req對(duì)象還沒(méi)有session這個(gè)屬性 app.use(session({ store: new RedisStore(options), secret: "express is powerful" })); // 經(jīng)過(guò)中間件處理后,可以通過(guò)req.session訪問(wèn)session object。比如如果你在session中保存了session.userId就可以根據(jù)userId查找用戶(hù)的信息了。
req在經(jīng)過(guò)session中間件的時(shí)候就會(huì)自動(dòng)完成session的有效性驗(yàn)證、延期/重新頒發(fā)、以及對(duì)session中數(shù)據(jù)的獲取了。
上述代碼只是對(duì)于請(qǐng)求的Session靜態(tài)處理,整個(gè)用戶(hù)管理的另一個(gè)方面則是狀態(tài)的切換(用戶(hù)的登陸、登出)以及用戶(hù)數(shù)據(jù)的獲取。
exports.signin = function(params, req, res){ var username = params.username; var password = params.password; //查找用戶(hù)信息,看是否滿足登陸條件 var user = findUser(username, password); if(user){ //成功獲取用戶(hù)對(duì)象 req.session.regenerate(function(){ req.user = user; req.session.userId = user.id; req.session.save(); //保存一下修改后的Session res.redirect("/account"); }); } else{ //用戶(hù)信息不符合,登陸失敗 } } exports.signout = function(req, res){ req.clearCookie("connect.sid"); req.user = null; req.session.regenerate(function(){ //重新生成session之后后續(xù)的處理 res.redirect("/signin"); }) } exports.persist = function(req, res, next){ var userId = req.session.userId; //通過(guò)user id去數(shù)據(jù)庫(kù)里面查找User對(duì)象 var user = findUserById(userId); if(user){ req.user = user; next(); } else{ //該用戶(hù)不存在 } }Session的安全問(wèn)題
SessionId就如同請(qǐng)求者的身份證,一旦被攻擊者惡意獲得,攻擊者便可以偽裝成請(qǐng)求者對(duì)服務(wù)器發(fā)起請(qǐng)求,也就是我們經(jīng)常聽(tīng)到的會(huì)話劫持(Session/Cookie Hijack)
關(guān)于會(huì)話劫持的原理推薦大家去看這篇文章
基于上述實(shí)現(xiàn)方法的Session管理,我認(rèn)為基本上可以排除
暴力破解SessionId
惡意植入固定SessionId
兩種可能,因?yàn)閡id的庫(kù)基本上可以保證SessionId的隨機(jī)性;而傳遞SessionId則依賴(lài)HTTP請(qǐng)求頭中的Cookie信息而非URL,同時(shí)在用戶(hù)登錄立刻更換SessionId。
唯一的可能性來(lái)源于Session的監(jiān)聽(tīng),而對(duì)于這種攻擊有效的兩種防止辦法是:
Https
很多網(wǎng)站僅僅在Login的階段使用Https防止用戶(hù)的用戶(hù)名、密碼信息被監(jiān)聽(tīng)者獲取,但是隨后的SessionId同樣有可能被監(jiān)聽(tīng)者獲取而偽造登錄者的身份信息。因此更加推薦的方式是所有的信息傳遞全部使用Https實(shí)現(xiàn),這樣即使監(jiān)聽(tīng)著截獲了信息也無(wú)法破解其中的內(nèi)容。關(guān)于如何使用NodeJS建立一個(gè)HTTPS的server可以參考《HTTPS的原理和NodeJS的實(shí)現(xiàn)》 這篇文章
httpOnly
Express在options中提供了httpOnly的屬性,此屬性默認(rèn)值為true,這個(gè)屬性保證了Cookie的信息不能夠通過(guò)JavaScript腳本獲取。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/85588.html
世界上任何一個(gè)擁有用戶(hù)數(shù)據(jù)的 web 應(yīng)用都必須處理 sessions。作為一名開(kāi)發(fā)者,我們必須要知道它們是什么以及如何處理它們。在這篇文章中,我想要分享的是: session 是什么? session 如何存儲(chǔ)數(shù)據(jù)? 你如何決定存放 session 數(shù)據(jù)的位置? 在 sessions 工作時(shí),你必須意識(shí)到的安全性上的影響有哪些?在一些示例代碼中,我將會(huì)運(yùn)用?session npm modul...
摘要:當(dāng)會(huì)話過(guò)期或被放棄后,服務(wù)器將終止該會(huì)話。原來(lái)中間件生成的是一個(gè)對(duì)象,里面包含了信息。這個(gè)有一個(gè)過(guò)期時(shí)間,比如,上面代碼中設(shè)置的是小時(shí)。也就是說(shuō),小時(shí)后,這個(gè)在瀏覽器中會(huì)自動(dòng)消失。 前言 在上一篇中node中的cookie,對(duì)cookie進(jìn)行了相關(guān)介紹,本篇將繼續(xù)前行,對(duì)session進(jìn)行說(shuō)明。 session是什么 session不就是會(huì)話嘛,那什么是會(huì)話呢?會(huì)話是一個(gè)比連接粒度更大...
摘要:當(dāng)會(huì)話過(guò)期或被放棄后,服務(wù)器將終止該會(huì)話。原來(lái)中間件生成的是一個(gè)對(duì)象,里面包含了信息。這個(gè)有一個(gè)過(guò)期時(shí)間,比如,上面代碼中設(shè)置的是小時(shí)。也就是說(shuō),小時(shí)后,這個(gè)在瀏覽器中會(huì)自動(dòng)消失。 前言 在上一篇中node中的cookie,對(duì)cookie進(jìn)行了相關(guān)介紹,本篇將繼續(xù)前行,對(duì)session進(jìn)行說(shuō)明。 session是什么 session不就是會(huì)話嘛,那什么是會(huì)話呢?會(huì)話是一個(gè)比連接粒度更大...
閱讀 3852·2021-11-11 11:02
閱讀 3562·2021-10-11 10:57
閱讀 3695·2021-09-22 16:00
閱讀 1973·2021-09-02 15:15
閱讀 1407·2019-08-30 15:56
閱讀 1090·2019-08-30 15:54
閱讀 2837·2019-08-30 12:43
閱讀 3621·2019-08-29 16:06