前端er應該懂的登陸態:Cookie、Session和Token

簡介

你們都知道,HTTP是一個無狀態的協議,那麼Web應用要怎麼保持用戶的登陸態呢?前端

若是你對cookiesessiontoken的優缺點不太明白,或者你想知道在實際中到底怎麼實現登陸態,那麼本文將很是適合你,本文將以發展歷程爲順序爲你們介紹cookiessession以及token的優點和缺點。node

本文知識點:git

  1. cookiesessiontoken(json web token,jwt)的區別
  2. nodejwt的應用

正文

咱們站在服務器這一端,一個用戶請求過來怎麼判斷他有沒有登陸呢?github

在驗證用戶名和密碼以後,咱們能夠發給客戶端一個憑證(isLogin = true),若是請求中有這個憑證,那麼他就是登錄以後的用戶。 cookiesession的區別在於,憑證的存儲位置。換言之,若是憑證存儲在客戶端,那就是cookie。若是憑證存儲在服務端,那就是sessionweb

客戶端存儲(cookie)

cookie實際上是HTTP頭部的一個字段,本質上能夠存儲任何信息,早年用於實現登陸態,因此有了一層別的含義——客戶端存儲。把憑證存儲到cookie中,每次瀏覽器的請求會自動帶上cookie裏的憑證,方便服務端校驗,就像下面這樣:算法

圖1 海綿寶寶請求調用`/login`接口,派大星驗證經過後給海綿寶寶頒發的登陸憑證`isLogin=true`express

可是這樣面臨的問題是:json

用戶本人能夠經過修改document.cookie="isLogin = true"僞造登錄憑證:跨域

圖2 海綿寶寶直接修改cookie跳過登陸接口驗證獲取數據瀏覽器

服務端存儲(session)

session本意是指客戶端與服務器的會話狀態,因爲憑證存儲到了服務端,後來也把這些存在服務端的信息稱爲session

如今服務器決定本身維護登陸狀態,僅發給客戶端一個key,而後在本身維護一個key-value表,若是請求中有key,而且在表中能夠找到對應的value,則視爲合法:

圖3 海綿寶寶請求調用`/login`接口,派大星驗證經過後給海綿寶寶頒發`sessionID`

這樣即便海綿寶寶自行修改了sessionID,在派大星那裏沒有對應的記錄,也沒法獲取數據。

session是一個好的解決方案,可是他的問題是:若是存在多個服務器如負載均衡時,每一個服務器的狀態表必須同步,或者抽離出來統一管理,如使用Redis等服務。

Token

還有其餘的方法能夠實現登錄態嗎?

cookie方法不須要服務器存儲,可是憑證容易被僞造,那有什麼辦法判斷憑證是否僞造呢?

HTTPS同樣,咱們可使用簽名的方式幫助服務器校驗憑證。

JSON Web Token(簡稱JWT)是以JSON格式存儲信息的Token,其結構圖以下:

圖4 JSON Web Token結構圖

JWT由3部分構成:頭部,負載和簽名。

根據官網介紹

  1. 頭部存儲Token的類型和簽名算法(上圖中,類型是jwt,加密算法是HS256
  2. 負載是Token要存儲的信息(上圖中,存儲了用戶姓名和暱稱信息)
  3. 簽名是由指定的算法,將轉義後的頭部和負載,加上密鑰一同加密獲得的。

最後將這三部分用.號鏈接,就能夠獲得了一個Token了。

使用JWT維護登錄態,服務器再也不須要維護狀態表,他僅給客戶端發送一個加密的數據token,每次請求都帶上這個加密的數據,再解密驗證是否合法便可。因爲是加密的數據,即便用戶能夠修改,命中概率也很小。

客戶端如何存儲token呢?

  1. 存在cookie中,雖然設置HttpOnly能夠有效防止XSS攻擊中token被竊取,可是也就意味着客戶端沒法獲取token來設置CORS頭部。
  2. 存在sessionStorage或者localStorage中,能夠設置頭部解決跨域資源共享問題,同時也能夠防止CSRF,可是就須要考慮XSS的問題防止憑證泄露。

NodeJWT的使用

Node中使用JWT只須要兩步:

第一步,在你的/login路由中使用jsonwebtoken中間件用於生成token

const jwt = require('jsonwebtoken')
let token = jwt.sign({
      name: user name
    }, config.secret, {
      expiresIn: '24h'
    })
    res.cookie('token', token)

複製代碼

具體使用方法請查看jsonwebtokenGithub

第二步,在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-jwtGithub

如何實現單點登陸

假設咱們在電腦和手機都使用同一個用戶登錄,對於服務器來講,這兩次登錄生成的token都是合法的,儘管他們是同一個用戶。因此兩個token不會失效。

要實現單點登錄,服務器只須要維護一張userIdtoken之間映射關係的表。每次登錄成功都刷新token的值。

在處理業務邏輯以前,使用解密拿到的userId去映射表中找到token,和請求中的token對比就能校驗是否合法了。

圖5 實現單點登陸

總結

實現登陸態是前端很是基礎且重要的技能之一。以前在學習這一塊的時候,分不清CookieSessionToken的區別。session是比cookie更好的一種解決方案。token成爲主流,是由於他不須要額外的存儲管理。可是當涉及到單點登陸的時候,其實也出現了多個服務器須要同步映射表的問題。

歡迎你們在評論區討論,指正!

參考

  1. 樸靈。《深刻淺出nodejs》。P181
  2. shanyue。jwt 實踐以及與 session 對比
相關文章
相關標籤/搜索