node.js學習(11)——cookie和session的使用

上一節-node.js學習(10)—express中間件—body-parser獲取post、get數據

上節我們講了如何在express中獲取post、get數據,這一節我們講解cookie和session。


20190523164210.png

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);
  1. 在express中,它已經為我們封裝好了操作cookie的方法,我們使用res.cookie來發送cookie
  2. 上面代碼表示在 /test路徑下,設置cookie為key:user,value:blue
  3. 如果設置多個cookie,需要寫多行res.cookie
  4. 除下設置key/value,還可以添加其他參數,如path,http,domain.maxAge,expires等,具體可以看express文檔-res.cookie
    res.cookie('user', 'blue',{
        path:'/',
        httpOnly:false
        ...
    })

啟動服務,打開瀏覽器并訪問/test路徑,我們可以在控制臺找到我們剛設置的cookie。

20190520163526.png

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);
  1. express默認需要引入cookie-parser模塊(安裝express會自動安裝,不用單獨安裝,只需要引入即可)才能獲取瀏覽器傳過來的cookie。
  2. 使用req.cookies獲取瀏覽器傳過來的cookie,值為對象。

在瀏覽器中運行上面代碼,如下圖:

  1. 第一次req.cookies為空,瀏覽器寫入cookie
  2. 第二次瀏覽器發送之前寫入的cookie,req.cookies有值
rrr.gif

3.cookie簽名(防服務器數據篡改)

何謂篡改,我們都知道,cookie是保存在客戶端的,我們可以通過js或者直接在控制臺修改cookie的,這樣會暴露一個問題:

  1. 瀏覽器發起一個修改用戶信息的請求,帶上Cookie信息username=aa(前提已經種下cookie),服務器接收到請求后,會去修改username為aa的數據
  2. 如果用戶知道username字段表示用戶名信息,那么他把值改為bb,然后再次發起請求,服務器接收到請求,去修改username為bb用戶的數據(假如數據庫中剛好有個username為bb的用戶)
  3. 這樣因為cookie被用戶修改,導致服務器數據被篡改的風險

怎么防止篡改?就有了cookie簽名的由來。

  1. 假設現在有個cookie(username=blue),服務器在發送cookie給瀏覽器之前先配置一個秘鑰(如sdfjk7777),通過sha256或者其他加密算法(這里假設加密方法為secret()),然后使用secret('blue','sdfjk7777')生成一個簽名和cookie值組合(類似{username:s:blue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks+6/zWRdXg}
  2. 其中blue表示真正的值,后面是簽名。假如我現在把blue改成blue1。
  3. 當瀏覽器再次發送請求時,服務器取出簽名(因為太長了,這里叫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);
  1. 將秘鑰 asdfd45556傳入cookieParser函數中,然后在需要簽名的cookie增加參數 signed:true
  2. 此時cookie會分割成兩個屬性 cookies-未簽名的、signedCookies-已簽名的
  3. 注意:req.signCookies的值為簽名的cookie原始值,不帶簽名字符串,閱讀cookie-parser源碼,其將簽名去除然后再賦到signCookies上
  4. 可以通過req.header.cookie獲取瀏覽器帶過來的cookie
20190521163546.png

上圖中,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);
  1. 使用session依賴cookie-session中間件,和cookie-parse不一樣,需要單獨安裝,express不帶。
  2. 該中間件使用必須傳一個key數組,用來的session進行簽名,和cookie簽名一樣,用來防止篡改,校驗。name可以不傳默認為express:sess
  3. 使用該中間件,session保存在cookie中,所以額外配置項就是cookie選項。所以如果expires/maxAge不傳,即N/A,會話cookie,瀏覽器關閉即過期。
  4. 引入該中間件,req上面會增加session屬性。demo中第一次進來session為空,不存在hasCome屬性(判斷是否已經來過),所以寫入該屬性,頁面返回歡迎第一次,后面再次訪問,req.session為{hasCome:true},頁面返回歡迎再次來,可以看下圖req.session打印狀態以及頁面展示內容
tesw.gif
  1. 我們通過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) 
//結果為
{}
  1. 從session的格式來看,簽名是單獨為一個sessionId.sig字段,和之前使用cookie-parser簽名一個cookie不一樣(之前直接在cookie上拼接簽名)
20190523154833.png
  1. 如果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);

頁面測試結果:

count1.gif

下一節-node.js學習(12)—nodejs模板引擎jade(1)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。