- 背景
做了四年的前端開發,對外一直說自己是web開發,那么身為一個web開發怎能不知道session與cookie以及其管理方式呢~
Login涉及技術棧:Nodejs,MongoDB,Express以及html,css,js
了解session與cookie之前首先要知道什么是http協議,為什么會出現session與cookie,可以參考很久之前總結的(戳我:session與cookie)。
- 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信息,服務器還是會記得我。
時間不早了。在這塊的知識還涉及什么時候token,token認證方式,以及什么是jwt。以后有時間會繼續更新的。
代碼地址:https://github.com/Dqhan/Login
大半夜的,寫個博客不容易,請博客園管理員高抬貴手,讓我留在首頁吧。
