在文章Cookie與登錄注冊中,我們已經(jīng)實(shí)現(xiàn)了簡單的注冊和登錄功能,并且登錄后服務(wù)器會給客戶端發(fā)送一個Cookie,登錄后跳轉(zhuǎn)首頁可以顯示自己的密碼。
但是有一個問題:現(xiàn)在的Cookie是明文的,用戶可以隨意篡改Cookie,繼而看到別人的密碼。
if (found) {
response.setHeader('Set-Cookie', `sign_in_email=${email}`)
response.statusCode = 200
}
上面代碼中,如果認(rèn)證用戶登錄成功,在Set-Cookie時會直接把email暴露給用戶。
Session可以解決這個問題。
首先在服務(wù)器中聲明一個空對象sessions(sessions是服務(wù)器的一塊內(nèi)存):(后端)
var sessions = {}
當(dāng)用戶登錄成功后,把一個隨機(jī)數(shù)sessionId通過Cookie傳到客戶端,并把這個sessionId和其對應(yīng)的用戶email存入session中。這樣,用戶不能直接從Cookie中看到email,但服務(wù)器仍然可以根據(jù)Cookie中的sessionId得到相匹配用戶信息。(后端)
if(found){
let sessionId = Math.random() * 1000000
session[sessionId] = {sign_in_email: email}
response.setHeader('Set-Cookie', `sessionId = ${sessionId}`)
response.statusCode = 200
}
當(dāng)用戶跳轉(zhuǎn)到首頁時,根據(jù)Cookie從sessions中找到匹配的email:(后端)
let cookies = '' //cookie初始為空字符串,避免沒有cookie時下面代碼中cookies.length報錯
if(request.headers.cookie){ //判斷是否有cookie,避免沒有cookie時split方法報錯
cookies = request.headers.cookie.split('; ') // ['email=1@', 'a=1', 'b=2']
}
let hash = {}
for (let i = 0; i < cookies.length; i++) {
let parts = cookies[i].split('=')
let key = parts[0]
let value = parts[1]
hash[key] = value
}
let email = sessions[hash.sessionId].sign_in_email
注意,現(xiàn)在用的是我們自己寫的node.js服務(wù)器,如果關(guān)閉服務(wù)器(每次改完服務(wù)器代碼都要重啟服務(wù)器),所有內(nèi)存會釋放,sessions就清空了(sessions是服務(wù)器中的一塊內(nèi)存)。一般服務(wù)器是不關(guān)閉的,如果要關(guān)閉,也會先把sessions存在硬盤中。
Session與Cookie的關(guān)系:
一般來說,Session基于Cookie來實(shí)現(xiàn)。
Cookie:
- 服務(wù)器通過響應(yīng)頭Set-Cookie給客戶端一串字符串
- 客戶端每次訪問相同域名的網(wǎng)頁時,請求頭必須帶上這段字符串
- 客戶端要在一段時間內(nèi)保存這個Cookie
- Cookie默認(rèn)在用戶關(guān)閉頁面后就失效,后臺代碼可以任意設(shè)置Cookie的過期時間
- 大小大概在4kb以內(nèi)
Session:
- 將SessionID(隨機(jī)數(shù))通過Cookie發(fā)給客戶端
- 客戶端訪問服務(wù)器時,服務(wù)器讀取SessionID
- 服務(wù)器有一塊內(nèi)存(哈希表)保存了所有的session
- 通過SessionID服務(wù)器可以得到對應(yīng)用戶的隱私信息,如id,Email
- 這塊內(nèi)存(哈希表)就是服務(wù)器上的所有session
Session可以用LocalStorage+查詢參數(shù)實(shí)現(xiàn)
一般來說,Session是基于Cookie實(shí)現(xiàn)的,因?yàn)镾ession將SessionID放在Cookie里發(fā)送給客戶端。
但是,也可以不使用Cookie,用LocalStorage+查詢參數(shù)來實(shí)現(xiàn)Session。
當(dāng)認(rèn)證用戶登錄成功后,不再Set-Cookies,而是直接把SessionID通過JSON傳給前端:(后端)
if(found){
let sessionId = Math.random() * 1000000
sessions[sessionId] = {sign_in_email: email}
response.write(`{"sessionId": ${sessionId}}`)
response.statusCode = 200
}
前端拿到SessionID后把它存到LocalStorage里備用(因?yàn)榭蛻舳艘谝欢螘r間內(nèi)持有這個SessionID),然后把SessionID作為查詢參數(shù)插入到要跳轉(zhuǎn)的頁面:(前端 sign_in.html)
$post('/sign_in', hash)
.then{(response) => {
let object = JSON.parse(response)
localStorage.setItem('sessionId', object.sessionId)
window.local.href = `/?sessionId=${object.sessionId}`
}, (request) =>{
alert('郵箱與密碼不匹配')
}}
再向首頁發(fā)起請求時,服務(wù)器從查詢參數(shù)中拿到SessionID,并在sessions中找到匹配的用戶信息:(后端)
let mySession = sessions[query.sessionId]
let email
if(mySession){
email = mySession.sign_in_email
}
完