完全理解 Cookie、Session、Token、JWT

做者:秋天不落頁
http://suo.im/5YKnRS


一、什麼是認證(Authentication)


  • 通俗地講就是 驗證當前用戶的身份,證實「你是你本身」(好比:你天天上下班打卡,都須要經過指紋打卡,當你的指紋和系統裏錄入的指紋相匹配時,就打卡成功)
  • 互聯網中的認證:
    • 用戶名密碼登陸
    • 郵箱發送登陸連接
    • 手機號接收驗證碼
    • 只要你能收到郵箱/驗證碼,就默認你是帳號的主人

二、什麼是受權(Authorization)


  • 用戶授予第三方應用訪問該用戶某些資源的權限
    • 你在安裝手機應用的時候,APP 會詢問是否容許授予權限(訪問相冊、地理位置等權限)
    • 你在訪問微信小程序時,當登陸時,小程序會詢問是否容許授予權限(獲取暱稱、頭像、地區、性別等我的信息)
  • 實現受權的方式有:cookie、session、token、OAuth


三、什麼是憑證(Credentials)


  • 實現認證和受權的前提是須要一種 媒介(證書) 來標記訪問者的身份
    • 在戰國時期,商鞅變法,發明了照身帖。照身帖由官府發放,是一塊打磨光滑細密的竹板,上面刻有持有人的頭像和籍貫信息。國人必須持有,如若沒有就被認爲是黑戶,或者間諜之類的。
    • 在現實生活中,每一個人都會有一張專屬的居民身份證,是用於證實持有人身份的一種法定證件。經過身份證,咱們能夠辦理手機卡/銀行卡/我的貸款/交通出行等等,這就是 認證的憑證。
    • 在互聯網應用中,通常網站(如掘金)會有兩種模式,遊客模式和登陸模式。遊客模式下,能夠正常瀏覽網站上面的文章,一旦想要點贊/收藏/分享文章,就須要登陸或者註冊帳號。當用戶登陸成功後,服務器會給該用戶使用的瀏覽器頒發一個令牌(token),這個令牌用來代表你的身份,每次瀏覽器發送請求時會帶上這個令牌,就可使用遊客模式下沒法使用的功能。

四、什麼是 Cookie


  • HTTP 是無狀態的協議(對於事務處理沒有記憶能力,每次客戶端和服務端會話完成時,服務端不會保存任何會話信息):每一個請求都是徹底獨立的,服務端沒法確認當前訪問者的身份信息,沒法分辨上一次的請求發送者和這一次的發送者是否是同一我的。因此服務器與瀏覽器爲了進行會話跟蹤(知道是誰在訪問我),就必須主動的去維護一個狀態,這個狀態用於告知服務端先後兩個請求是否來自同一瀏覽器。而這個狀態須要經過 cookie 或者 session 去實現。
  • cookie 存儲在客戶端: cookie 是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器下次向同一服務器再發起請求時被攜帶併發送到服務器上。
  • cookie 是不可跨域的: 每一個 cookie 都會綁定單一的域名,沒法在別的域名下獲取使用, 一級域名和二級域名之間是容許共享使用的靠的是 domain)

cookie 重要的屬性


五、什麼是 Session

  • session 是另外一種記錄服務器和客戶端會話狀態的機制
  • session 是基於 cookie 實現的,session 存儲在服務器端,sessionId 會被存儲到客戶端的cookie 中


  • session 認證流程:
    • 用戶第一次請求服務器的時候,服務器根據用戶提交的相關信息,建立對應的 Session
    • 請求返回時將此 Session 的惟一標識信息 SessionID 返回給瀏覽器
    • 瀏覽器接收到服務器返回的 SessionID 信息後,會將此信息存入到 Cookie 中,同時 Cookie 記錄此 SessionID 屬於哪一個域名
    • 當用戶第二次訪問服務器的時候,請求會自動判斷此域名下是否存在 Cookie 信息,若是存在自動將 Cookie 信息也發送給服務端,服務端會從 Cookie 中獲取 SessionID,再根據 SessionID 查找對應的 Session 信息,若是沒有找到說明用戶沒有登陸或者登陸失效,若是找到 Session 證實用戶已經登陸可執行後面操做。

根據以上流程可知, SessionID 是鏈接 Cookie 和 Session 的一道橋樑,大部分系統也是根據此原理來驗證用戶登陸狀態。

Cookie 和 Session 的區別


  • 安全性: Session 比 Cookie 安全,Session 是存儲在服務器端的,Cookie 是存儲在客戶端的。
  • 存取值的類型不一樣:Cookie 只支持存字符串數據,想要設置其餘類型的數據,須要將其轉換成字符串,Session 能夠存任意數據類型。
  • 有效期不一樣: Cookie 可設置爲長時間保持,好比咱們常用的默認登陸功能,Session 通常失效時間較短,客戶端關閉(默認狀況下)或者 Session 超時都會失效。
  • 存儲大小不一樣: 單個 Cookie 保存的數據不能超過 4K,Session 可存儲數據遠高於 Cookie,可是當訪問量過多,會佔用過多的服務器資源。

六、什麼是 Token(令牌)


Acesss Token

  • 訪問資源接口(API)時所須要的資源憑證
  • 簡單 token 的組成: uid(用戶惟一的身份標識)、time(當前時間的時間戳)、sign(簽名,token 的前幾位以哈希算法壓縮成的必定長度的十六進制字符串)
  • 特色:
    • 服務端無狀態化、可擴展性好
    • 支持移動端設備
    • 安全
    • 支持跨程序調用
  • token 的身份驗證流程:


  1. 客戶端使用用戶名跟密碼請求登陸
  2. 服務端收到請求,去驗證用戶名與密碼
  3. 驗證成功後,服務端會簽發一個 token 並把這個 token 發送給客戶端
  4. 客戶端收到 token 之後,會把它存儲起來,好比放在 cookie 裏或者 localStorage 裏
  5. 客戶端每次向服務端請求資源的時候須要帶着服務端簽發的 token
  6. 服務端收到請求,而後去驗證客戶端請求裏面帶着的 token ,若是驗證成功,就向客戶端返回請求的數據
  • 每一次請求都須要攜帶 token,須要把 token 放到 HTTP 的 Header 裏
  • 基於 token 的用戶認證是一種服務端無狀態的認證方式,服務端不用存放 token 數據。用解析 token 的計算時間換取 session 的存儲空間,從而減輕服務器的壓力,減小頻繁的查詢數據庫
  • token 徹底由應用管理,因此它能夠避開同源策略

Refresh Token


  • 另一種 token——refresh token
  • refresh token 是專用於刷新 access token 的 token。若是沒有 refresh token,也能夠刷新 access token,但每次刷新都要用戶輸入登陸用戶名與密碼,會很麻煩。有了 refresh token,能夠減小這個麻煩,客戶端直接用 refresh token 去更新 access token,無需用戶進行額外的操做。


  • Access Token 的有效期比較短,當 Acesss Token 因爲過時而失效時,使用 Refresh Token 就能夠獲取到新的 Token,若是 Refresh Token 也失效了,用戶就只能從新登陸了。
  • Refresh Token 及過時時間是存儲在服務器的數據庫中,只有在申請新的 Acesss Token 時纔會驗證,不會對業務接口響應時間形成影響,也不須要向 Session 同樣一直保持在內存中以應對大量的請求。

Token 和 Session 的區別


  • Session 是一種 記錄服務器和客戶端會話狀態的機制,使服務端有狀態化,能夠記錄會話信息。而 Token 是 令牌訪問資源接口(API)時所須要的資源憑證。Token 使服務端無狀態化,不會存儲會話信息。
  • Session 和 Token 並不矛盾,做爲身份認證 Token 安全性比 Session 好,由於每個請求都有簽名還能防止監聽以及重放攻擊,而 Session 就必須依賴鏈路層來保障通信安全了。 若是你須要實現有狀態的會話,仍然能夠增長 Session 來在服務器端保存一些狀態。
  • 所謂 Session 認證只是簡單的把 User 信息存儲到 Session 裏,由於 SessionID 的不可預測性,暫且認爲是安全的。而 Token ,若是指的是 OAuth Token 或相似的機制的話,提供的是 認證 和 受權 ,認證是針對用戶,受權是針對 App 。其目的是讓某 App 有權利訪問某用戶的信息。這裏的 Token 是惟一的。不能夠轉移到其它 App上,也不能夠轉到其它用戶上。Session 只提供一種簡單的認證,即只要有此 SessionID ,即認爲有此 User 的所有權利。是須要嚴格保密的,這個數據應該只保存在站方,不該該共享給其它網站或者第三方 App。因此簡單來講: 若是你的用戶數據可能須要和第三方共享,或者容許第三方調用 API 接口,用 Token 。若是永遠只是本身的網站,本身的 App,用什麼就無所謂了。

七、什麼是 JWT


  • JSON Web Token(簡稱 JWT)是目前最流行的 跨域認證解決方案。
  • 是一種 認證受權機制
  • JWT 是爲了在網絡應用環境間 傳遞聲明而執行的一種基於 JSON 的開放標準(RFC 7519)。JWT 的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源。好比用在用戶登陸上。
  • 可使用 HMAC 算法或者是 RSA 的公/私祕鑰對 JWT 進行簽名。由於數字簽名的存在,這些傳遞的信息是可信的。
  • 阮一峯老師的 JSON Web Token 入門教程 講的很是通俗易懂,這裏就再也不班門弄斧了

生成 JWT


  • https://jwt.io/
  • https://www.jsonwebtoken.io/


JWT 的原理


  • JWT 認證流程:
    • 用戶輸入用戶名/密碼登陸,服務端認證成功後,會返回給客戶端一個 JWT
    • 客戶端將 token 保存到本地(一般使用 localstorage,也可使用 cookie)
    • 當用戶但願訪問一個受保護的路由或者資源的時候,須要請求頭的 Authorization 字段中使用Bearer 模式添加 JWT,其內容看起來是下面這樣
   
   
    
    
             
    
    
Authorization: Bearer <token>
  • 服務端的保護路由將會檢查請求頭 Authorization 中的 JWT 信息,若是合法,則容許用戶的行爲
  • 由於 JWT 是自包含的(內部包含了一些會話信息),所以減小了須要查詢數據庫的須要
  • 由於 JWT 並不使用 Cookie 的,因此你可使用任何域名提供你的 API 服務而不須要擔憂跨域資源共享問題(CORS)
  • 由於用戶的狀態再也不存儲在服務端的內存中,因此這是一種無狀態的認證機制

JWT 的使用方式


  • 客戶端收到服務器返回的 JWT,能夠儲存在 Cookie 裏面,也能夠儲存在 localStorage。

方式一


  • 當用戶但願訪問一個受保護的路由或者資源的時候,能夠把它放在 Cookie 裏面自動發送,可是這樣不能跨域,因此更好的作法是放在 HTTP 請求頭信息的 Authorization 字段裏,使用 Bearer 模式添加 JWT。
         

         
         
          
          
                   
          
          
    GET /calendar/v1/eventsHost: api.example.comAuthorization: Bearer <token>
    • 用戶的狀態不會存儲在服務端的內存中,這是一種 無狀態的認證機制
    • 服務端的保護路由將會檢查請求頭 Authorization 中的 JWT 信息,若是合法,則容許用戶的行爲。
    • 因爲 JWT 是自包含的,所以減小了須要查詢數據庫的須要
    • JWT 的這些特性使得咱們能夠徹底依賴其無狀態的特性提供數據 API 服務,甚至是建立一個下載流服務。
    • 由於 JWT 並不使用 Cookie ,因此你可使用任何域名提供你的 API 服務而 不須要擔憂跨域資源共享問題(CORS)

方式二


  • 跨域的時候,能夠把 JWT 放在 POST 請求的數據體裏。

方式三


  • 經過 URL 傳輸
   
   
    
    
             
    
    
http://www.example.com/user?token=xxx
  
  
   
   
            
   
   

項目中使用 JWT


項目地址:https://github.com/yjdjiayou/jwt-demo

Token 和 JWT 的區別


相同:
  • 都是訪問資源的令牌
  • 均可以記錄用戶的信息
  • 都是使服務端無狀態化
  • 都是隻有驗證成功後,客戶端才能訪問服務端上受保護的資源

區別:
  • Token:服務端驗證客戶端發送過來的 Token 時,還須要查詢數據庫獲取用戶信息,而後驗證 Token 是否有效。
  • JWT:將 Token 和 Payload 加密後存儲於客戶端,服務端只須要使用密鑰解密進行校驗(校驗也是 JWT 本身實現的)便可,不須要查詢或者減小查詢數據庫,由於 JWT 自包含了用戶信息和加密的數據。

八、常見的先後端鑑權方式


  1. Session-Cookie
  2. Token 驗證(包括 JWT,SSO)
  3. OAuth2.0(開放受權)

九、常見的加密算法


  • 哈希算法(Hash Algorithm)又稱散列算法、散列函數、哈希函數,是一種從任何一種數據中建立小的數字「指紋」的方法。哈希算法將數據從新打亂混合,從新建立一個哈希值。
  • 哈希算法主要用來保障數據真實性(即完整性),即發信人將原始消息和哈希值一塊兒發送,收信人經過相同的哈希函數來校驗原始數據是否真實。
  • 哈希算法一般有如下幾個特色:
    • 2 的 128 次方爲 340282366920938463463374607431768211456,也就是 10 的 39 次方級別
    • 2 的 160 次方爲 1.4615016373309029182036848327163e+48,也就是 10 的 48 次方級別
    • 2 的 256 次方爲 1.1579208923731619542357098500869 × 10 的 77 次方,也就是 10 的 77 次方
    • 正像快速:原始數據能夠快速計算出哈希值
    • 逆向困難:經過哈希值基本不可能推導出原始數據
    • 輸入敏感:原始數據只要有一點變更,獲得的哈希值差異很大
    • 衝突避免:很難找到不一樣的原始數據獲得相同的哈希值,宇宙中原子數大約在 10 的 60 次方到 80 次方之間,因此 2 的 256 次方有足夠的空間容納全部的可能,算法好的狀況下衝突碰撞的機率很低:

注意:

  1. 以上不能保證數據被惡意篡改,原始數據和哈希值均可能被惡意篡改,要保證不被篡改,可使用RSA 公鑰私鑰方案,再配合哈希值。
  2. 哈希算法主要用來防止計算機傳輸過程當中的錯誤,早期計算機經過前 7 位數據第 8 位奇偶校驗碼來保障(12.5% 的浪費效率低),對於一段數據或文件,經過哈希算法生成 128bit 或者 256bit 的哈希值,若是校驗有問題就要求重傳。

十、常見問題


使用 cookie 時須要考慮的問題


  • 由於存儲在客戶端,容易被客戶端篡改,使用前須要驗證合法性
  • 不要存儲敏感數據,好比用戶密碼,帳戶餘額
  • 使用 httpOnly 在必定程度上提升安全性
  • 儘可能減小 cookie 的體積,能存儲的數據量不能超過 4kb
  • 設置正確的 domain 和 path,減小數據傳輸
  • cookie 沒法跨域
  • 一個瀏覽器針對一個網站最多存 20 個Cookie,瀏覽器通常只容許存放 300 個Cookie
  • 移動端對 cookie 的支持不是很好,而 session 須要基於 cookie 實現,因此移動端經常使用的是 token

使用 session 時須要考慮的問題


  • 將 session 存儲在服務器裏面,當用戶同時在線量比較多時,這些 session 會佔據較多的內存,須要在服務端按期的去清理過時的 session
  • 當網站採用 集羣部署的時候,會遇到多臺 web 服務器之間如何作 session 共享的問題。由於 session 是由單個服務器建立的,可是處理用戶請求的服務器不必定是那個建立 session 的服務器,那麼該服務器就沒法拿到以前已經放入到 session 中的登陸憑證之類的信息了。
  • 當多個應用要共享 session 時,除了以上問題,還會遇到跨域問題,由於不一樣的應用可能部署的主機不同,須要在各個應用作好 cookie 跨域的處理。
  • sessionId 是存儲在 cookie 中的,假如瀏覽器禁止 cookie 或不支持 cookie 怎麼辦? 通常會把 sessionId 跟在 url 參數後面即重寫 url,因此 session 不必定非得須要靠 cookie 實現
  • 移動端對 cookie 的支持不是很好,而 session 須要基於 cookie 實現,因此移動端經常使用的是 token

使用 token 時須要考慮的問題


  • 若是你認爲用數據庫來存儲 token 會致使查詢時間太長,能夠選擇放在內存當中。好比 redis 很適合你對 token 查詢的需求。
  • token 徹底由應用管理,因此它能夠避開同源策略
  • token 能夠避免 CSRF 攻擊(由於不須要 cookie 了)
  • 移動端對 cookie 的支持不是很好,而 session 須要基於 cookie 實現,因此移動端經常使用的是 token

使用 JWT 時須要考慮的問題


  • 由於 JWT 並不依賴 Cookie 的,因此你可使用任何域名提供你的 API 服務而不須要擔憂跨域資源共享問題(CORS)
  • JWT 默認是不加密,但也是能夠加密的。生成原始 Token 之後,能夠用密鑰再加密一次。
  • JWT 不加密的狀況下,不能將祕密數據寫入 JWT。
  • JWT 不只能夠用於認證,也能夠用於交換信息。有效使用 JWT,能夠下降服務器查詢數據庫的次數。
  • JWT 最大的優點是服務器再也不須要存儲 Session,使得服務器認證鑑權業務能夠方便擴展。但這也是 JWT 最大的缺點:因爲服務器不須要存儲 Session 狀態,所以使用過程當中沒法廢棄某個 Token 或者更改 Token 的權限。也就是說一旦 JWT 簽發了,到期以前就會始終有效,除非服務器部署額外的邏輯。
  • JWT 自己包含了認證信息,一旦泄露,任何人均可以得到該令牌的全部權限。爲了減小盜用,JWT的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。
  • JWT 適合一次性的命令認證,頒發一個有效期極短的 JWT,即便暴露了危險也很小,因爲每次操做都會生成新的 JWT,所以也不必保存 JWT,真正實現無狀態。
  • 爲了減小盜用,JWT 不該該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

使用加密算法時須要考慮的問題


  • 毫不要以 明文存儲密碼
  • 永遠使用 哈希算法 來處理密碼,毫不要使用 Base64 或其餘編碼方式來存儲密碼,這和以明文存儲密碼是同樣的,使用哈希,而不要使用編碼。編碼以及加密,都是雙向的過程,而密碼是保密的,應該只被它的全部者知道, 這個過程必須是單向的。哈希正是用於作這個的,歷來沒有解哈希這種說法, 可是編碼就存在解碼,加密就存在解密。
  • 毫不要使用弱哈希或已被破解的哈希算法,像 MD5 或 SHA1 ,只使用強密碼哈希算法。
  • 毫不要以明文形式顯示或發送密碼,即便是對密碼的全部者也應該這樣。若是你須要 「忘記密碼」 的功能,能夠隨機生成一個新的 一次性的(這點很重要)密碼,而後把這個密碼發送給用戶。

分佈式架構下 session 共享方案


1. session 複製

  • 任何一個服務器上的 session 發生改變(增刪改),該節點會把這個 session 的全部內容序列化,而後廣播給全部其它節點,無論其餘服務器需不須要 session ,以此來保證 session 同步
優勢: 可容錯,各個服務器間 session 可以實時響應。
缺點: 會對網絡負荷形成必定壓力,若是 session 量大的話可能會形成網絡堵塞,拖慢服務器性能。

2. 粘性 session /IP 綁定策略

  • 採用 Ngnix 中的 ip_hash 機制,將某個 ip的全部請求都定向到同一臺服務器上,即將用戶與服務器綁定。 用戶第一次請求時,負載均衡器將用戶的請求轉發到了 A 服務器上,若是負載均衡器設置了粘性 session 的話,那麼用戶之後的每次請求都會轉發到 A 服務器上,至關於把用戶和 A 服務器粘到了一塊,這就是粘性 session 機制。
優勢: 簡單,不須要對 session 作任何處理。
缺點: 缺少容錯性,若是當前訪問的服務器發生故障,用戶被轉移到第二個服務器上時,他的 session 信息都將失效。
適用場景: 發生故障對客戶產生的影響較小;服務器發生故障是低機率事件。
實現方式: 以 Nginx 爲例,在 upstream 模塊配置 ip_hash 屬性便可實現粘性 session。

3. session 共享(經常使用)

  • 使用分佈式緩存方案好比 Memcached 、Redis 來緩存 session,可是要求 Memcached 或 Redis 必須是集羣
  • 把 session 放到 Redis 中存儲,雖然架構上變得複雜,而且須要多訪問一次 Redis ,可是這種方案帶來的好處也是很大的:
    • 實現了 session 共享;
    • 能夠水平擴展(增長 Redis 服務器);
    • 服務器重啓 session 不丟失(不過也要注意 session 在 Redis 中的刷新/失效機制);
    • 不只能夠跨服務器 session 共享,甚至能夠跨平臺(例如網頁端和 APP 端)

4. session 持久化

  • 將 session 存儲到數據庫中,保證 session 的持久化
優勢: 服務器出現問題,session 不會丟失
缺點: 若是網站的訪問量很大,把 session 存儲到數據庫中,會對數據庫形成很大壓力,還須要增長額外的開銷維護數據庫。

只要關閉瀏覽器 ,session 真的就消失了?


不對。對 session 來講,除非程序通知服務器刪除一個 session,不然服務器會一直保留,程序通常都是在用戶作 log off 的時候發個指令去刪除 session。

然而瀏覽器歷來不會主動在關閉以前通知服務器它將要關閉,所以服務器根本不會有機會知道瀏覽器已經關閉,之因此會有這種錯覺,是大部分 session 機制都使用會話 cookie 來保存 session id,而關閉瀏覽器後這個 session id 就消失了,再次鏈接服務器時也就沒法找到原來的 session。

若是服務器設置的 cookie 被保存在硬盤上,或者使用某種手段改寫瀏覽器發出的 HTTP 請求頭,把原來的 session id 發送給服務器,則再次打開瀏覽器仍然可以打開原來的 session。

偏偏是 因爲關閉瀏覽器不會致使 session 被刪除,迫使服務器爲 session 設置了一個失效時間,當距離客戶端上一次使用 session 的時間超過這個失效時間時,服務器就認爲客戶端已經中止了活動,纔會把 session 刪除以節省存儲空間。

項目地址:https://github.com/yjdjiayou/jwt-demo


本文只是基於本身的理解講了理論知識,由於對後端/算法知識不是很熟,若有謬誤,還請告知,萬分感謝!


若是本文對你有所幫助,還請點個贊~~

近期
幾行代碼摸清楚上拉加載原理

10款谷歌瀏覽器插件武裝你的瀏覽器
javascript


若此文有用,何不素質三連❤️

本文分享自微信公衆號 - Vue中文社區(vue_fe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。vue

相關文章
相關標籤/搜索