在文章Cookie與登錄注冊中,我們已經(jīng)實現(xiàn)了簡單的注冊和登錄功能,并且登錄后服務器會給客戶端發(fā)送一個Cookie,登錄后跳轉首頁可以顯示自己的密碼。
但是有一個問題:現(xiàn)在的Cookie是明文的,用戶可以隨意篡改Cookie,繼而看到別人的密碼。
if (found) {
response.setHeader('Set-Cookie', `sign_in_email=${email}`)
response.statusCode = 200
}
上面代碼中,如果認證用戶登錄成功,在Set-Cookie時會直接把email暴露給用戶。
Session可以解決這個問題。
首先在服務器中聲明一個空對象sessions(sessions是服務器的一塊內(nèi)存):(后端)
var sessions = {}
當用戶登錄成功后,把一個隨機數(shù)sessionId通過Cookie傳到客戶端,并把這個sessionId和其對應的用戶email存入session中。這樣,用戶不能直接從Cookie中看到email,但服務器仍然可以根據(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
}
當用戶跳轉到首頁時,根據(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服務器,如果關閉服務器(每次改完服務器代碼都要重啟服務器),所有內(nèi)存會釋放,sessions就清空了(sessions是服務器中的一塊內(nèi)存)。一般服務器是不關閉的,如果要關閉,也會先把sessions存在硬盤中。
Session與Cookie的關系:
一般來說,Session基于Cookie來實現(xiàn)。
Cookie:
- 服務器通過響應頭Set-Cookie給客戶端一串字符串
- 客戶端每次訪問相同域名的網(wǎng)頁時,請求頭必須帶上這段字符串
- 客戶端要在一段時間內(nèi)保存這個Cookie
- Cookie默認在用戶關閉頁面后就失效,后臺代碼可以任意設置Cookie的過期時間
- 大小大概在4kb以內(nèi)
Session:
- 將SessionID(隨機數(shù))通過Cookie發(fā)給客戶端
- 客戶端訪問服務器時,服務器讀取SessionID
- 服務器有一塊內(nèi)存(哈希表)保存了所有的session
- 通過SessionID服務器可以得到對應用戶的隱私信息,如id,Email
- 這塊內(nèi)存(哈希表)就是服務器上的所有session
Session可以用LocalStorage+查詢參數(shù)實現(xiàn)
一般來說,Session是基于Cookie實現(xiàn)的,因為Session將SessionID放在Cookie里發(fā)送給客戶端。
但是,也可以不使用Cookie,用LocalStorage+查詢參數(shù)來實現(xiàn)Session。
當認證用戶登錄成功后,不再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里備用(因為客戶端要在一段時間內(nèi)持有這個SessionID),然后把SessionID作為查詢參數(shù)插入到要跳轉的頁面:(前端 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ā)起請求時,服務器從查詢參數(shù)中拿到SessionID,并在sessions中找到匹配的用戶信息:(后端)
let mySession = sessions[query.sessionId]
let email
if(mySession){
email = mySession.sign_in_email
}
完