上一節-node.js學習(10)—express中間件—body-parser獲取post、get數據
上節我們講了如何在express中獲取post、get數據,這一節我們講解cookie和session。
1.給瀏覽器發送( response ) cookie
我們在server.js添加如下代碼
const express=require('express');
const server=express();
server.use('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
res.cookie('user', 'blue'); //發送cookie給瀏覽器,讓瀏覽器來寫入cookie
res.send('hello cookie');
});
server.listen(8080);
- 在express中,它已經為我們封裝好了操作cookie的方法,我們使用
res.cookie
來發送cookie - 上面代碼表示在
/test
路徑下,設置cookie為key:user,value:blue
- 如果設置多個cookie,需要寫多行
res.cookie
- 除下設置key/value,還可以添加其他參數,如path,http,domain.maxAge,expires等,具體可以看express文檔-res.cookie
res.cookie('user', 'blue',{
path:'/',
httpOnly:false
...
})
啟動服務,打開瀏覽器并訪問/test
路徑,我們可以在控制臺找到我們剛設置的cookie。
2.服務器接收瀏覽器傳過來的( request ) cookie
上面我們講了服務端如何發送cookie給瀏覽器,并且也知道瀏覽器訪問服務器url,會先檢查url下有沒有cookie,有的話在訪問過程中,會將cookie自動添加到請求頭中,那么服務器如何從請求頭中獲取cookie呢?
我們修改server.js
const express=require('express');
const cookieParser=require('cookie-parser');
const server=express();
server.use(cookieParser())
server.use('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
res.cookie('user', 'blue'); //設置cookie
res.cookie('user1', 'blue1'); //設置cookie
console.log(req.cookies) //{"user": "blue","user1":"blue1"}
res.send(req.cookies);
});
server.listen(8080);
- express默認需要引入
cookie-parser
模塊(安裝express會自動安裝,不用單獨安裝,只需要引入即可)才能獲取瀏覽器傳過來的cookie。 - 使用
req.cookies
獲取瀏覽器傳過來的cookie,值為對象。
在瀏覽器中運行上面代碼,如下圖:
- 第一次req.cookies為空,瀏覽器寫入cookie
- 第二次瀏覽器發送之前寫入的cookie,req.cookies有值
3.cookie簽名(防服務器數據篡改)
何謂篡改,我們都知道,cookie是保存在客戶端的,我們可以通過js或者直接在控制臺修改cookie的,這樣會暴露一個問題:
- 瀏覽器發起一個修改用戶信息的請求,帶上Cookie信息username=aa(前提已經種下cookie),服務器接收到請求后,會去修改username為aa的數據
- 如果用戶知道username字段表示用戶名信息,那么他把值改為bb,然后再次發起請求,服務器接收到請求,去修改username為bb用戶的數據(假如數據庫中剛好有個username為bb的用戶)
- 這樣因為cookie被用戶修改,導致服務器數據被篡改的風險
怎么防止篡改?就有了cookie簽名的由來。
- 假設現在有個cookie(username=blue),服務器在發送cookie給瀏覽器之前先配置一個秘鑰(如sdfjk7777),通過sha256或者其他加密算法(這里假設加密方法為secret()),然后使用
secret('blue','sdfjk7777')
生成一個簽名和cookie值組合(類似{username:s:blue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks+6/zWRdXg}
- 其中blue表示真正的值,后面是簽名。假如我現在把blue改成blue1。
- 當瀏覽器再次發送請求時,服務器取出簽名(因為太長了,這里叫A)以及cookie真正的值(blue1)。然后使用之前的加密算法判斷secret('blue1','sdfjk7777')==A,如果不相等,說明cookie被惡意修改了。
4.使用cookie-parser增加cookie簽名
我們修改server.js
const express=require('express');
const cookieParser=require('cookie-parser');
const server=express();
//增加一個隨機秘鑰
server.use(cookieParser('asdfd45556'))
server.use('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
res.cookie('user', 'blue',{
signed:true
});
res.cookie('user1', 'blue1');
console.log(req.cookies); //無簽名cookie {user1:blue1}
console.log(req.signedCookies); //簽名cookie {user:blue}
console.log(req.headers.cookie) //瀏覽器發過來的cookie: user1=blue1; user=s%3Ablue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks%2B6%2FzWRdXg
res.send('ok'); //由于res.send默認最后一個生效(相當于只能寫一個),所以上面用console.log代替輸出
});
server.listen(8080);
- 將秘鑰
asdfd45556
傳入cookieParser函數中,然后在需要簽名的cookie增加參數signed:true
- 此時cookie會分割成兩個屬性 cookies-未簽名的、signedCookies-已簽名的
- 注意:req.signCookies的值為簽名的cookie原始值,不帶簽名字符串,閱讀cookie-parser源碼,其將簽名去除然后再賦到signCookies上
- 可以通過req.header.cookie獲取瀏覽器帶過來的cookie
上圖中,s%3Ablue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks%2B6%2FzWRdXg為簽名cookie,實際上是該值是先簽名后encodeURIComponent編碼過來的。我們可以解碼看
console.log(decodeURIComponent('s%3Ablue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks%2B6%2FzWRdXg'))
//s:blue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks+6/zWRdXg
其中,s:
表示簽名,blue
為真正的值,后面的是簽名
所以我們在瀏覽器實際上仍可以看見用戶信息,cookie簽名并不能起到加密作用,只是防篡改作用。
5.cookie-session
之前在cookie和session區別曾講過seesion。下面我們通過nodejs來實現session的demo(判斷用戶是否來過,保持用戶登陸狀態)
server.js代碼如下:
const express=require('express');
// const cookieParser=require('cookie-parser');
const cookieSession=require('cookie-session');
const server=express();
// server.use(cookieParser())
server.use(cookieSession({
keys: ['aaa', 'bbb', 'ccc'], //session用來生成簽名的列表,必填
name: 'sessionId', //session名字,可省略,實測默認名字為'express:sess'
cookie:{ //session配置
//cookie選項
path:'',
...
}
}))
server.get('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
console.log(req.session); //第一次為{},后面為{hasCome:true}
// console.log(req.cookies);
// console.log(req.signedCookies);
if(!req.session['hasCome']){
req.session['hasCome']=true //寫入hasCome屬性
res.send('歡迎第一次');
}else{
res.send('歡迎再次來');
}
});
server.listen(8080);
- 使用session依賴
cookie-session
中間件,和cookie-parse不一樣,需要單獨安裝,express不帶。 - 該中間件使用必須傳一個key數組,用來的session進行簽名,和cookie簽名一樣,用來防止篡改,校驗。name可以不傳默認為
express:sess
- 使用該中間件,session保存在cookie中,所以額外配置項就是cookie選項。所以如果expires/maxAge不傳,即N/A,會話cookie,瀏覽器關閉即過期。
- 引入該中間件,req上面會增加session屬性。demo中第一次進來session為空,不存在
hasCome
屬性(判斷是否已經來過),所以寫入該屬性,頁面返回歡迎第一次
,后面再次訪問,req.session為{hasCome:true}
,頁面返回歡迎再次來
,可以看下圖req.session
打印狀態以及頁面展示內容
- 我們通過
req.session
可以很方便的訪問session,由于該session對應的id是保存在cookie中,所以我們仍可以引用cookie-parser在cookie中查看sessionId(注意:雖然sessionId有簽名,但是該插件將sessionId存在req.cookies
屬性中,不在req.signedCookies
中,所以通過req.signedCookies
是無法訪問到的)
現在我們把之前server.js的代碼中注釋的cookie-parser等代碼解開注釋,可以看到打印結果:
console.log(req.cookies)
//結果為
{
sessionId: 'eyJoYXNDb21lIjp0cnVlfQ==',
'sessionId.sig': 'moOOM66p-CAR9aeTORKkuBYXFk4'
}
console.log(req.signCookies)
//結果為
{}
- 從session的格式來看,簽名是單獨為一個
sessionId.sig
字段,和之前使用cookie-parser簽名一個cookie不一樣(之前直接在cookie上拼接簽名)
- 如果
session
值變化,sessionId
以及簽名sessionId.sig
會跟著變化。我們再換個demo(記錄頁面訪問次數)測試下
更改server.js代碼:
const express=require('express');
const cookieSession=require('cookie-session');
const server=express();
server.use(cookieSession({
keys: ['aaa', 'bbb', 'ccc'],
name: 'sessionId',
}))
server.get('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
//count字段表示頁面訪問次數
if(!req.session['count']){
req.session['count']=1
}else{
req.session['count']++
}
res.send(''+req.session['count'])
});
server.listen(8080);
頁面測試結果: