全面了解cookie和session
- http協議:
http即超文本傳輸協議(萬維網定義的),一種基於瀏覽器請求與服務器響應的鏈接,它是一個很純粹的傳輸協議。http協議主要的特征就是它是一種無狀態的協議(只針對cookie與session問題),在客戶端連續向服務器發送請求的時候,每次請求的過程中只要數據交換完畢,服務器與客戶端就會斷開連接,再次請求的時候會重新連接客戶端與服務器,這樣服務器記錄上次的對話,那麽問題來了,如何讓服務器知道是哪個客戶端向自己發出的請求呢,這個時候cookie就誕生了~
- 什麽是cookie
cookie是一小段文本信息,這段小文本信息由服務器首次響應客戶端時發送的,在客戶端向服務器首次發送請求的時候,服務器會判斷是否要記錄客戶端的身份,如果需要,此時就會在響應中(response)給客戶端發送一個cookie,該cookie文本信息保存在http的報頭裏,當瀏覽器會將cookie保存起來,當該瀏覽器再次發送請求時會攜帶cookie,服務器檢查cookie來識別瀏覽器請求,這裏cookie的特征就不在說明了。下面我們上代碼!
頁面代碼:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <script type="text/javascript" src="jquery-3.3.1.min.js"></script> <script type="text/javascript" src=‘test.js‘></script> </head> <body> <section> <h3>Register</h3> <div> <label style="display:inline-block; width: 100px;" id="register-user-name-label" htmlfor="register-user-name-input">register:</label> <input style="display:inline-block; width: 200px;" id="register-user-name-input" type="text" /> </div> <div> <label style="display:inline-block; width: 100px;" id="register-password-label" htmlfor="register-password-input">pasword:</label> <input style="display:inline-block; width: 200px;" id="register-password-input" type="text" /> </div> <button id="register" type="button">Register</button> </section> <section> <h3>Login</h3> <div> <label style="display:inline-block; width: 100px;" id="user-name-label" htmlfor="user-name-input">login name:</label> <input style="display:inline-block; width: 200px;" id="user-name-input" type="text" /> </div> <div> <label style="display:inline-block; width: 100px;" id="password-label" htmlfor="password-input">pasword:</label> <input style="display:inline-block; width: 200px;" id="password-input" type="text" /> </div> <button id="login" type="button">Login</button> </section> <script type="text/javascript" src=‘test.js‘></script> </body> </html>
很簡單,一個註冊按鈕一個登陸按鈕(ps:代碼冗余請忽視,就是為了做個demo用)。
首先註冊一個user:
註冊user之後我們查看db
然後我們用這個user進行登陸操作,重點來啦~
首先刷新下頁面,調用獲取user方法,看下效果
代碼如下:
app.get(‘/userInfo‘, function (req, res) { //cookie if (req.cookies.userInfo) { console.log(‘login successfully‘) } else { console.log(‘session timeout.‘); } res.status(200).json(req.cookies.userInfo); //session // if (req.session.userInfo) console.log(‘login successfully‘); // else console.log(‘session timeout.‘); })
好了先mark下,回頭再來做對比,下面執行login操作,這裏要上代碼了。
首先引入一個中間件:
var cookie = require(‘cookie-parser‘);
使用它:
app.use(cookie(‘express_cookie‘)); //cookie app.post(‘/login‘, function (req, res) { User.findOne({ username: req.body.username }).then(function (userInfo) { if (!userInfo) { console.log(‘user is not exist.‘); return; } var data = {}; data[‘username‘] = userInfo.username; data[‘password‘] = userInfo.password; res.cookie(‘username‘, JSON.stringify(data), { maxAge: 900000, httpOnly: true }); res.status(200).json(data); }) .catch(function (e) { console.log(e); }) })
這裏我們可以設置cookie的httpOnly屬性,最大生命周期,等等,然後我們先在db內查詢當前登錄user,如果已經註冊過,我們獲取user信息並存入cookie中。這時候看下前端的響應有什麽不同。
可以看見,服務器頒發的cookie在響應的header中的Set-Cookie中。似不似發現不同了。這時候我們在刷新下頁面調用userInfo方法看下效果。
咦,我們發現這次的請求裏面居然有cookie了,就這麽神奇(Ps:我們要相信科學!)。
debug下看看
服務器端有我們想要的cookie信息了。這樣服務器就可以根據cookie知道了我們每一次的請求是不是同一個人了。
總結:首先cookie是服務器頒發的,然後隨著響應返回給客戶端也就是我們的瀏覽器,瀏覽器保存cookie,每一次發送請求都會帶著這個cookie來讓服務器知道,嗯我就是上次的那個人,到這裏對cookie是不是多少了解了一些呢~
好了,那麽現在很多瀏覽器都是禁用cookie的,原因是啥呢~,由於cookie是可以被獲取的以及cookie是可以修改的,這時候引出了web安全方面的姿勢,跨站腳本攻擊以及跨站協議偽造,可以參考我之前寫的關於XSS攻擊(戳我:什麽是XSS以及CFRS),那麽如果cookie禁用了我們該怎麽辦呢?這時候session就誕生了。
- 何為session:
session本省並不存在,只是一個概念,session是服務器用來記錄客戶端狀態的機制,不同於cookie保存在瀏覽器中,session是保存在服務器上的,服務器會根據cookie生成一個session id存在服務器上,當請求再次抵達服務器時,服務器發出響應時會將session id 存在cookie內一同反回給瀏覽器,這就是session。session具體哪些特點這裏就不寫啦,話不多說,上代碼。
首先引入一個中間件:
var session = require(‘express-session‘);
使用它
app.use(cookie(‘express_cookie‘)); app.use(session({ secret: ‘express_cookie‘, resave: false, saveUninitialized: true, cookie: { maxAge: 60 * 1000 * 30 }, rolling: true, })); app.post(‘/login‘, function (req, res) { User.findOne({ username: req.body.username }).then(function (userInfo) { if (!userInfo) { console.log(‘user is not exist.‘); return; } var data = {}; data[‘username‘] = userInfo.username; data[‘password‘] = userInfo.password; req.session.userInfo = data; res.status(200).json(data); }) .catch(function (e) { console.log(e); }) })
現在我們登錄一下看下效果:
會發現,多了一個Cookie,而且Cookie裏面多了一個sid,不用聯想了,這就是sessionId,這時候我們在刷新一下頁面看下userInfo變成啥樣了呢?
可以清晰的看到再次請求的時候,sessionId會裝在Cookie中,然後發送給服務器,這時候服務器就知道了,咦,原來是上個人。這就是session。
由於現在服務器session存入的方式我們采用了服務器自帶的內存,也叫session memory。如果server掛了怎麽辦呢,掛掉了內存就釋放了啊,session就沒了啊。這個時候就引出了另外一個問題,session的可持續化。
- session的可持續化
session的可持續化方式簡單的理解就是讓session可以在生命周期內一直存在,可以把session存入db中,可以是MongoDB,可以是redis,上代碼,我們這裏用MongoDB吧,個人比較喜愛。
引入中間件:
var MongoStore = require(‘connect-mongo‘)(session);
app.use(cookie(‘express_cookie‘)); app.use(session({ secret: ‘express_cookie‘, resave: false, saveUninitialized: true, cookie: { maxAge: 60 * 1000 * 30 }, rolling: true, store: new MongoStore({ url: ‘mongodb://127.0.0.1:27017/demo‘, collection: ‘sessions‘ }) })); app.post(‘/login‘, function (req, res) { User.findOne({ username: req.body.username }).then(function (userInfo) { if (!userInfo) { console.log(‘user is not exist.‘); return; } var data = {}; data[‘username‘] = userInfo.username; data[‘password‘] = userInfo.password; req.session.userInfo = data; res.status(200).json(data); }) .catch(function (e) { console.log(e); }) })
http請求與響應部分我們就不看了,直接看server跟DB。
server中我們將userInfo放入session中
var data = {}; data[‘username‘] = userInfo.username; data[‘password‘] = userInfo.password; req.session.userInfo = data;
查看DB
咦,一條session就在DB中誕生了,這裏要註意的是,session不是設置的時候就會存入DB中的,包括內存等等,而且響應成功的時候才會存入,一定要註意,不然坑的就是你。
然後刷新頁面看下效果。
似不似,session中就有了user信息。好了到這裏關於session持久化的問題也解決了。
登出功能就很簡單了,銷毀session就ok了,代碼如下:
app.get("/loginOut",function(req,res){ req.session.destroy(function(err){ console.log(err); }) res.send(‘退出登錄成功‘); });
Redis方式:
中間件以及使用:
var RedisStrore = require(‘connect-redis‘)(session); app.use(session({ secret: ‘express_cookie‘, resave: false, saveUninitialized: true, cookie: { maxAge: 60 * 1000 * 30 }, rolling: true, store: new RedisStrore({}) }));
總結:第一次登陸請求的時候,服務器會頒發一個sessionId,響應的時候將sessionId放入cookie中返回給瀏覽器,此時session已存入DB中,當再次請求的時候攜帶著sessionId進入服務器中,獲取session信息,服務器還是會記得我
全面了解cookie和session