你們都知道,HTTP
是一個無狀態的協議,那麼Web應用要怎麼保持用戶的登陸態呢?前端
若是你對cookie
,session
和token
的優缺點不太明白,或者你想知道在實際中到底怎麼實現登陸態,那麼本文將很是適合你,本文將以發展歷程爲順序爲你們介紹cookies
,session
以及token
的優點和缺點。node
本文知識點:git
cookie
,session
,token(json web token,jwt)
的區別node
中jwt
的應用咱們站在服務器這一端,一個用戶請求過來怎麼判斷他有沒有登陸呢?github
在驗證用戶名和密碼以後,咱們能夠發給客戶端一個憑證(isLogin = true),若是請求中有這個憑證,那麼他就是登錄以後的用戶。 cookie
和session
的區別在於,憑證的存儲位置。換言之,若是憑證存儲在客戶端,那就是cookie
。若是憑證存儲在服務端,那就是session
。web
cookie
實際上是HTTP
頭部的一個字段,本質上能夠存儲任何信息,早年用於實現登陸態,因此有了一層別的含義——客戶端存儲。把憑證存儲到cookie
中,每次瀏覽器的請求會自動帶上cookie
裏的憑證,方便服務端校驗,就像下面這樣:算法
圖1 海綿寶寶請求調用`/login`接口,派大星驗證經過後給海綿寶寶頒發的登陸憑證`isLogin=true`express
可是這樣面臨的問題是:json
用戶本人能夠經過修改document.cookie="isLogin = true"
僞造登錄憑證:跨域
圖2 海綿寶寶直接修改cookie跳過登陸接口驗證獲取數據瀏覽器
session
本意是指客戶端與服務器的會話狀態,因爲憑證存儲到了服務端,後來也把這些存在服務端的信息稱爲session
。
如今服務器決定本身維護登陸狀態,僅發給客戶端一個key
,而後在本身維護一個key-value
表,若是請求中有key
,而且在表中能夠找到對應的value
,則視爲合法:
圖3 海綿寶寶請求調用`/login`接口,派大星驗證經過後給海綿寶寶頒發`sessionID`
這樣即便海綿寶寶自行修改了sessionID
,在派大星那裏沒有對應的記錄,也沒法獲取數據。
session
是一個好的解決方案,可是他的問題是:若是存在多個服務器如負載均衡時,每一個服務器的狀態表必須同步,或者抽離出來統一管理,如使用Redis
等服務。
還有其餘的方法能夠實現登錄態嗎?
cookie
方法不須要服務器存儲,可是憑證容易被僞造,那有什麼辦法判斷憑證是否僞造呢?
和HTTPS
同樣,咱們可使用簽名的方式幫助服務器校驗憑證。
JSON Web Token(簡稱JWT)
是以JSON
格式存儲信息的Token
,其結構圖以下:
圖4 JSON Web Token結構圖
JWT
由3部分構成:頭部,負載和簽名。
根據官網介紹:
Token
的類型和簽名算法(上圖中,類型是jwt
,加密算法是HS256
)Token
要存儲的信息(上圖中,存儲了用戶姓名和暱稱信息)最後將這三部分用.
號鏈接,就能夠獲得了一個Token
了。
使用JWT
維護登錄態,服務器再也不須要維護狀態表,他僅給客戶端發送一個加密的數據token
,每次請求都帶上這個加密的數據,再解密驗證是否合法便可。因爲是加密的數據,即便用戶能夠修改,命中概率也很小。
客戶端如何存儲token
呢?
cookie
中,雖然設置HttpOnly
能夠有效防止XSS
攻擊中token
被竊取,可是也就意味着客戶端沒法獲取token
來設置CORS
頭部。sessionStorage
或者localStorage
中,能夠設置頭部解決跨域資源共享問題,同時也能夠防止CSRF
,可是就須要考慮XSS
的問題防止憑證泄露。Node
中JWT
的使用在Node
中使用JWT
只須要兩步:
第一步,在你的/login
路由中使用jsonwebtoken
中間件用於生成token
:
const jwt = require('jsonwebtoken')
let token = jwt.sign({
name: user name
}, config.secret, {
expiresIn: '24h'
})
res.cookie('token', token)
複製代碼
具體使用方法請查看jsonwebtoken
的Github
第二步,在Node
的入口文件app.js
中註冊express-jwt
中間件用於驗證token
:
const expressJwt = require('express-jwt')
app.use(expressJwt({
secret: config.secret,
getToken: (req) => {
return req.cookies.token || null
}
}).unless({
path: [
'/login'
]
}))
複製代碼
若是getToken
返回null
,中間件會拋出UnauthorizedError
異常:
app.use(function (err, req, res, next) {
//當token驗證失敗時會拋出以下錯誤
if (err.name === 'UnauthorizedError') {
res.status(401).json({
status: 'fail',
message: '身份校驗過時,請從新登錄'
});
}
});
複製代碼
具體使用語法參考express-jwt
的Github
假設咱們在電腦和手機都使用同一個用戶登錄,對於服務器來講,這兩次登錄生成的token
都是合法的,儘管他們是同一個用戶。因此兩個token
不會失效。
要實現單點登錄,服務器只須要維護一張userId
和token
之間映射關係的表。每次登錄成功都刷新token
的值。
在處理業務邏輯以前,使用解密拿到的userId
去映射表中找到token
,和請求中的token
對比就能校驗是否合法了。
圖5 實現單點登陸
實現登陸態是前端很是基礎且重要的技能之一。以前在學習這一塊的時候,分不清Cookie
,Session
和Token
的區別。session
是比cookie
更好的一種解決方案。token
成爲主流,是由於他不須要額外的存儲管理。可是當涉及到單點登陸的時候,其實也出現了多個服務器須要同步映射表的問題。
歡迎你們在評論區討論,指正!