閱讀更多系列文章請訪問我的GitHub博客,示例代碼請訪問這裏。
Cookie介紹
Cookie存儲在瀏覽器,在瀏覽器請求服務器時,其中的數據都會被髮送到服務端,常用來做用戶信息校驗等。
但由於Cookie存儲在瀏覽器,容易受到篡改,安全性較差。
使用cookie-parser處理Cookie
處理Cookie,可以使用中間件cookie-parser
讀取Cookie
示例代碼:/lesson05/server.js
使用cookie-parser中間件時,需要先通過server.use(cookieParser())
解析cookie,之後就可以在req.cookies
屬性中讀取到cookie的值。
在瀏覽器打開http://localhost:8080/cookie,在控制檯寫入cookie{"userName":"lee"}
。
// 使用cookie-parser中間件,解析Cookie
server.use(cookieParser())
server.get('/cookie', (req, res, next) => {
// 讀取cookieParser解析的Cookie
console.log(req.cookies)
res.send(`cookies: ${JSON.stringify(req.cookies)}`)
})
在瀏覽器打開http://localhost:8080/cookie,服務端打印結果爲:{"userName":"lee"}
。
設置Cookie
示例代碼:/lesson05/server.js
設置Cookie可以用Express自帶的方法res.cookie。
方法的第一個參數爲設置的屬性名,第二個參數爲屬性值,第三個參數爲配置項,例如:
server.get('/cookie', (req, res, next) => {
// express自帶的設置Cookie方法
res.cookie('userName', 'lee', {
// 設置該Cookie只可以由服務端訪問,即前端JavaScript無法訪問document.cookie獲取該值,但控制檯還是可以查看和修改
httpOnly: true,
// 只有通過HTTPS請求的Cookie才被使用,否則都認爲是錯誤的Cookie
// secure: true,
// 設置保存Cookie的域名,瀏覽器查找Cookie時,子域名(如translate.google.com)可以訪問主域名(google.com)下的Cookie,而主域名(google.com)不可以訪問子域名(如translate.google.com)下的Cookie
// 本地測試可直接設置爲localhost
domain: 'localhost',
// 設置保存Cookie的路徑,瀏覽器查找Cookie時,子路徑(如/map)可以訪問根路徑('/')下設置的Cookie,而根路徑('/')無法訪問子路徑(如/map)下設置的Cookie
path: '/',
// 通過expires設置Cookie過期時間爲14天后
// expires: new Date(new Date().getTime() + 14 * 86400000),
// 通過maxAge設置Cookie過期時間爲14天后
maxAge: 14 * 86400000,
})
// 讀取cookieParser解析的Cookie
console.log(req.cookies)
res.send(`cookies: ${JSON.stringify(req.cookies)}`)
})
在瀏覽器的控制檯中,可以看到設置的Cookie爲{"userName":"lee"}
,有效期是14天。
Cookie的簽名
示例代碼:/lesson05/server.js
Cookie的簽名即是使用一個存儲在服務端的密鑰對Cookie進行加密,Cookie中存儲的數據是經過密鑰加密的,因此客戶端如果對Cookie進行修改,服務端校驗就無法通過。
設置密鑰
若需要給Cookie進行簽名,首先需要給cookieParser的第一個參數傳入一個字符串密鑰:
// 解析Cookie
server.use(cookieParser(
// 簽名用密鑰,需要保密,僅存儲在服務端
'NpLRTpy1vbBzEw2JcAxpf970kOk2RViDn5wKwrMv'
))
簽名Cookie
這樣就可以開始設置簽名Cookie了,只需要在res.cookie
方法的配置參數中設置signed: true
屬性:
res.cookie('password', 'test123', {
httpOnly: true,
domain: 'localhost',
path: '/',
maxAge: 14 * 86400000,
// 開啓該Cookie的簽名模式
signed: true
})
在瀏覽器打開http://localhost:8080/cookie,可以看到Cookie中被設置了password
屬性,其值爲s%3Atest123.HrZ44MCUeLXj0uZAzTpCXWduflOsmfBs5XsuK4eTMvg
。
如果用decodeURIComponent方法進行解碼,結果爲s:test123.HrZ44MCUeLXj0uZAzTpCXWduflOsmfBs5XsuK4eTMvg
。
其意義如下:
s
表示該Cookie爲簽名Cookietest123
表示該Cookie設置的值HrZ44MCUeLXj0uZAzTpCXWduflOsmfBs5XsuK4eTMvg
表示對該值的簽名,也就是說當服務端接收到該Cookie時,會使用服務端的密鑰對test123
進行簽名,再與HrZ44MCUeLXj0uZAzTpCXWduflOsmfBs5XsuK4eTMvg
進行對比,如果正確纔可以使用。
此時可以看到服務端打印結果爲signedCookies: {"password":"test123"}
。
在客戶端修改簽名的Cookie
如果用戶在客戶端對簽名的Cookie進行了修改,例如在瀏覽器控制檯,將password改爲s%3Atest456.HrZ44MCUeLXj0uZAzTpCXWduflOsmfBs5XsuK4eTMvg
。
此時在服務端打印的結果爲signedCookies: {"password":false}
,表示校驗失敗。
同時瀏覽器中的Cookie值被重新修改爲了s%3Atest123.HrZ44MCUeLXj0uZAzTpCXWduflOsmfBs5XsuK4eTMvg
。
除非用戶在修改簽名的Cookie的值時,將該值的簽名一起修改,否則校驗是無法通過的。
但由於簽名是由服務端的密鑰計算而成,因此這個值通常是安全的。
不過,因爲對Cookie進行簽名,會佔用更多的Cookie存儲空間,而Cookie在瀏覽器中最多隻能存儲4K,所以簽名不可濫用,只用來保護重要的數據即可。