主頁 > 後端開發 > JWT 身份認證優缺點分析以及常見問題解決方案

JWT 身份認證優缺點分析以及常見問題解決方案

2020-12-05 06:45:43 後端開發

之前分享了一個使用 Spring Security 實作 JWT 身份認證的 Demo,文章地址:適合初學者入門 Spring Security With JWT 的 Demo, Demo 非常簡單,沒有介紹到 JWT 存在的一些問題,所以,單獨抽了一篇文章出來介紹,為了完成這篇文章,我查閱了很多資料和文獻,我覺得應該對大家有幫助,

相關閱讀:

  • 《一問帶你區分清楚Authentication,Authorization以及Cookie、Session、Token》
  • 適合初學者入門 Spring Security With JWT 的 Demo
  • Spring Boot 使用 JWT 進行身份和權限驗證

Token 認證的優勢

相比于 Session 認證的方式來說,使用 token 進行身份認證主要有下面三個優勢:

1.無狀態

token 自身包含了身份驗證所需要的所有資訊,使得我們的服務器不需要存盤 Session 資訊,這顯然增加了系統的可用性和伸縮性,大大減輕了服務端的壓力,但是,也正是由于 token 的無狀態,也導致了它最大的缺點:當后端在token 有效期內廢棄一個 token 或者更改它的權限的話,不會立即生效,一般需要等到有效期過后才可以,另外,當用戶 Logout 的話,token 也還有效,除非,我們在后端增加額外的處理邏輯,

2.有效避免了CSRF 攻擊

CSRF(Cross Site Request Forgery)一般被翻譯為 跨站請求偽造,屬于網路攻擊領域范圍,相比于 SQL 腳本注入、XSS等等安全攻擊方式,CSRF 的知名度并沒有它們高,但是,它的確是每個系統都要考慮的安全隱患,就連技術帝國 Google 的 Gmail 在早些年也被曝出過存在 CSRF 漏洞,這給 Gmail 的用戶造成了很大的損失,

那么究竟什么是 跨站請求偽造 呢?說簡單用你的身份去發送一些對你不友好的請求,舉個簡單的例子:

小壯登錄了某網上銀行,他來到了網上銀行的帖子區,看到一個帖子下面有一個鏈接寫著“科學理財,年盈利率過萬”,小壯好奇的點開了這個鏈接,結果發現自己的賬戶少了10000元,這是這么回事呢?原來黑客在鏈接中藏了一個請求,這個請求直接利用小壯的身份給銀行發送了一個轉賬請求,也就是通過你的 Cookie 向銀行發出請求,

<a src="http://www.mybank.com/Transfer?bankId=11&money=10000">科學理財,年盈利率過萬</a>

導致這個問題很大的原因就是: Session 認證中 Cookie 中的 session_id 是由瀏覽器發送到服務端的,借助這個特性,攻擊者就可以通過讓用戶誤點攻擊鏈接,達到攻擊效果,

那為什么 token 不會存在這種問題呢?

我是這樣理解的:一般情況下我們使用 JWT 的話,在我們登錄成功獲得 token 之后,一般會選擇存放在 local storage 中,然后我們在前端通過某些方式會給每個發到后端的請求加上這個 token,這樣就不會出現 CSRF 漏洞的問題,因為,即使有個你點擊了非法鏈接發送了請求到服務端,這個非法請求是不會攜帶 token 的,所以這個請求將是非法的,

但是這樣會存在 XSS 攻擊中被盜的風險,為了避免 XSS 攻擊,你可以選擇將 token 存盤在標記為httpOnly 的cookie 中,但是,這樣又導致了你必須自己提供CSRF保護,

具體采用上面哪兩種方式存盤 token 呢,大部分情況下存放在 local storage 下都是最好的選擇,某些情況下可能需要存放在標記為httpOnly 的cookie 中會更好,

3.適合移動端應用

使用 Session 進行身份認證的話,需要保存一份資訊在服務器端,而且這種方式會依賴到 Cookie(需要 Cookie 保存 SessionId),所以不適合移動端,

但是,使用 token 進行身份認證就不會存在這種問題,因為只要 token 可以被客戶端存盤就能夠使用,而且 token 還可以跨語言使用,

4.單點登錄友好

使用 Session 進行身份認證的話,實作單點登錄,需要我們把用戶的 Session 資訊保存在一臺電腦上,并且還會遇到常見的 Cookie 跨域的問題,但是,使用 token 進行認證的話, token 被保存在客戶端,不會存在這些問題,

Token 認證常見問題以及解決辦法

1.注銷登錄等場景下 token 還有效

與之類似的具體相關場景有:

  1. 退出登錄;
  2. 修改密碼;
  3. 服務端修改了某個用戶具有的權限或者角色;
  4. 用戶的帳戶被洗掉/暫停,
  5. 用戶由管理員注銷;

這個問題不存在于 Session 認證方式中,因為在 Session 認證方式中,遇到這種情況的話服務端洗掉對應的 Session 記錄即可,但是,使用 token 認證的方式就不好解決了,我們也說過了,token 一旦派發出去,如果后端不增加其他邏輯的話,它在失效之前都是有效的,那么,我們如何解決這個問題呢?查閱了很多資料,總結了下面幾種方案:

  • 將 token 存入記憶體資料庫:將 token 存入 DB 中,redis 記憶體資料庫在這里是是不錯的選擇,如果需要讓某個 token 失效就直接從 redis 中洗掉這個 token 即可,但是,這樣會導致每次使用 token 發送請求都要先從 DB 中查詢 token 是否存在的步驟,而且違背了 JWT 的無狀態原則,
  • 黑名單機制:和上面的方式類似,使用記憶體資料庫比如 redis 維護一個黑名單,如果想讓某個 token 失效的話就直接將這個 token 加入到 黑名單 即可,然后,每次使用 token 進行請求的話都會先判斷這個 token 是否存在于黑名單中,
  • 修改密鑰 (Secret) : 我們為每個用戶都創建一個專屬密鑰,如果我們想讓某個 token 失效,我們直接修改對應用戶的密鑰即可,但是,這樣相比于前兩種引入記憶體資料庫帶來了危害更大,比如: ① 如果服務是分布式的,則每次發出新的 token 時都必須在多臺機器同步密鑰,為此,你需要將必須將機密存盤在資料庫或其他外部服務中,這樣和 Session 認證就沒太大區別了,② 如果用戶同時在兩個瀏覽器打開系統,或者在手機端也打開了系統,如果它從一個地方將賬號退出,那么其他地方都要重新進行登錄,這是不可取的,
  • 保持令牌的有效期限短并經常輪換 :很簡單的一種方式,但是,會導致用戶登錄狀態不會被持久記錄,而且需要用戶經常登錄,

對于修改密碼后 token 還有效問題的解決還是比較容易的,說一種我覺得比較好的方式:使用用戶的密碼的哈希值對 token 進行簽名,因此,如果密碼更改,則任何先前的令牌將自動無法驗證,

2.token 的續簽問題

token 有效期一般都建議設定的不太長,那么 token 過期后如何認證,如何實作動態重繪 token,避免用戶經常需要重新登錄?

我們先來看看在 Session 認證中一般的做法:假如 session 的有效期30分鐘,如果 30 分鐘內用戶有訪問,就把 session 有效期被延長30分鐘,

  1. 類似于 Session 認證中的做法:這種方案滿足于大部分場景,假設服務端給的 token 有效期設定為30分鐘,服務端每次進行校驗時,如果發現 token 的有效期馬上快過期了,服務端就重新生成 token 給客戶端,客戶端每次請求都檢查新舊token,如果不一致,則更新本地的token,這種做法的問題是僅僅在快過期的時候請求才會更新 token ,對客戶端不是很友好,
  2. 每次請求都回傳新 token :這種方案的的思路很簡單,但是,很明顯,開銷會比較大,
  3. token 有效期設定到半夜 :這種方案是一種折衷的方案,保證了大部分用戶白天可以正常登錄,適用于對安全性要求不高的系統,
  4. 用戶登錄回傳兩個 token :第一個是 acessToken ,它的過期時間 token 本身的過期時間比如半個小時,另外一個是 refreshToken 它的過期時間更長一點比如為1天,客戶端登錄后,將 accessToken和refreshToken 保存在本地,每次訪問將 accessToken 傳給服務端,服務端校驗 accessToken 的有效性,如果過期的話,就將 refreshToken 傳給服務端,如果有效,服務端就生成新的 accessToken 給客戶端,否則,客戶端就重新登錄即可,該方案的不足是:① ?需要客戶端來配合;② ?用戶注銷的時候需要同時保證兩個 token 都無效;③ 重新請求獲取 token 的程序中會有短暫 token 不可用的情況(可以通過在客戶端設定定時器,當accessToken 快過期的時候,提前去通過 refreshToken 獲取新的accessToken),

總結

JWT 最適合的場景是不需要服務端保存用戶狀態的場景,比如如果考慮到 token 注銷和 token 續簽的場景話,沒有特別好的解決方案,大部分解決方案都給 token 加上了狀態,這就有點類似 Session 認證了,

Reference

  • JWT 超詳細分析
  • https://medium.com/devgorilla/how-to-log-out-when-using-jwt-a8c7823e8a6
  • https://medium.com/@agungsantoso/csrf-protection-with-json-web-tokens-83e0f2fcbcc
  • Invalidating JSON Web Tokens

作者:Snailclimb
鏈接:JWT 身份認證優缺點分析以及常見問題解決方案
來源:github

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230140.html

標籤:Java

上一篇:C/C++編程筆記:“ int main()”和“ int main(void)”的區別?

下一篇:Java Number類, Character類,String類

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more