前端面試查漏補缺--(十) 前端鑑權

shotCat發表於2019-02-23

前言

本系列最開始是為了自己面試準備的.後來發現整理越來越多,差不多有十二萬字元,最後決定還是分享出來給大家.

為了分享整理出來,花費了自己大量的時間,起碼是隻自己用的三倍時間.如果喜歡的話,歡迎收藏,關注我!謝謝!

文章連結

合集篇:

前端面試查漏補缺--Index篇(12萬字元合集) 包含目前已寫好的系列其他十幾篇文章.後續新增值文章不會再在每篇新增連結,強烈建議議點贊,關注合集篇!!!!,謝謝!~

後續更新計劃

後續還會繼續新增設計模式,前端工程化,專案流程,部署,閉環,vue常考知識點 等內容.如果覺得內容不錯的話歡迎收藏,關注我!謝謝!

求一份內推

目前本人也在準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的武漢 前端崗位!郵箱:bupabuku@foxmail.com.謝謝啦!~

常見鑒權方式

目前我們常用的鑑權有四種:

  1. HTTP Basic Authentication (HTTP基本認證)
  2. session-cookie
  3. Token 驗證(包括JWT,SSO)
  4. OAuth(開放授權)

HTTP Basic Authentication

這種認證方式是瀏覽器遵守http協議實現的基本授權方式,HTTP協議進行通訊的過程中,HTTP協議定義了基本認證認證允許HTTP伺服器對客戶端進行使用者身份證的方法。

目前基本沒有再使用這種認證方式的,一些老專案的內網認證可能還會有.

這裡大概提一下驗證過程,主要參考這篇文章

認證過程:   1. 客戶端向伺服器請求資料,請求的內容可能是一個網頁或者是一個ajax非同步請求,此時,假設客戶端尚未被驗證,則客戶端提供如下請求至伺服器:

  Get /index.html HTTP/1.0 
  Host:www.google.com
複製程式碼

2. 伺服器向客戶端傳送驗證請求程式碼401,(WWW-Authenticate: Basic realm=”google.com”這句話是關鍵,如果沒有客戶端不會彈出使用者名稱和密碼輸入介面)伺服器返回的資料大抵如下:

  HTTP/1.0 401 Unauthorised 
  Server: SokEvo/1.0 
  WWW-Authenticate: Basic realm=”google.com” 
  Content-Type: text/html 
  Content-Length: xxx
複製程式碼

3. 當符合http1.0或1.1規範的客戶端(如IE,FIREFOX)收到401返回值時,將自動彈出一個登入視窗,要求使用者輸入使用者名稱和密碼。

4. 使用者輸入使用者名稱和密碼後,將使用者名稱及密碼以BASE64加密方式加密(base64不安全!),並將密文放入前一條請求資訊中,則客戶端傳送的第一條請求資訊則變成如下內容:

  Get /index.html HTTP/1.0 
  Host:www.google.com 
  Authorization: Basic d2FuZzp3YW5n
複製程式碼

注:d2FuZzp3YW5n表示加密後的使用者名稱及密碼(使用者名稱:密碼 然後通過base64加密,加密過程是瀏覽器預設的行為,不需要我們人為加密,我們只需要輸入使用者名稱密碼即可)

5. 伺服器收到上述請求資訊後,將Authorization欄位後的使用者資訊取出、解密,將解密後的使用者名稱及密碼與使用者資料庫進行比較驗證,如使用者名稱及密碼正確,伺服器則根據請求,將所請求資源傳送給客戶端

效果:
客戶端未未認證的時候,會彈出使用者名稱密碼輸入框,這個時候請求時屬於pending狀態, 這個時候其實服務當使用者輸入使用者名稱密碼的時候客戶端會再次傳送帶Authentication頭的請求。

session-cookie

這個方式是利用伺服器端的session(會話)和瀏覽器端的cookie來實現前後端的認證,由於http請求時是無狀態的,伺服器正常情況下是不知道當前請求之前有沒有來過,這個時候我們如果要記錄狀態,就需要在伺服器端建立一個會話(session),將同一個客戶端的請求都維護在各自得會會話中,每當請求到達伺服器端的時候,先去查一下該客戶端有沒有在伺服器端建立session,如果有則已經認證成功了,否則就沒有認證。

認證過程:

1,伺服器在接受客戶端首次訪問時在伺服器端建立session,然後儲存session(我們可以將session儲存在記憶體中,也可以儲存在redis中,推薦使用後者),然後給這個session生成一個唯一的標識字串,然後在響應頭中種下這個唯一標識字串。

2.簽名。這一步只是對sid進行加密處理,服務端會根據這個secret金鑰進行解密。(非必需步驟)

3.瀏覽器中收到請求響應的時候會解析響應頭,然後將sid儲存在本地cookie中,瀏覽器在下次http請求的請求頭中會帶上該域名下的cookie資訊,

4.伺服器在接受客戶端請求時會去解析請求頭cookie中的sid,然後根據這個sid去找伺服器端儲存的該客戶端的session,然後判斷該請求是否合法。

弊端:

  • 伺服器記憶體消耗大: 使用者每做一次應用認證,應用就會在服務端做一次記錄,以方便使用者下次請求時使用,通常來講session儲存在記憶體中,隨著認證使用者的增加,伺服器的消耗就會很大.
  • 易受到CSRF攻擊: 基於cookie的一種跨站偽造攻擊, 基於cookie來進行識別使用者的話,使用者本身就攜帶了值,cookie被截獲,使用者就很容易被偽造.

Token驗證

概念

token是使用者身份的驗證方式,我們通常叫它:令牌。當使用者第一次登入後,伺服器生成一個token並將此token返回給客戶端,以後客戶端只需帶上這個token前來請求資料即可,無需再次帶上使用者名稱和密碼。

最簡單的token組成:uid(使用者唯一的身份標識)、time(當前時間的時間戳)、sign(簽名,由token的前幾位+鹽以雜湊演算法壓縮成一定長的十六進位制字串,可以防止惡意第三方拼接token請求伺服器)。還可以把不變的引數也放進token,避免多次查庫。

我們可以把Token想象成一個安全的護照。你在一個安全的前臺驗證你的身份(通過你的使用者名稱和密碼),如果你成功驗證了自己,你就可以取得這個。當你走進大樓的時候(試圖從呼叫API獲取資源),你會被要求驗證你的護照,而不是在前臺重新驗證。

驗證流程

大概的流程是這樣的:

  • 1, 客戶端使用使用者名稱跟密碼請求登入
  • 2, 服務端收到請求,去驗證使用者名稱與密碼
  • 3, 驗證成功後,服務端會簽發一個 Token,再把這個 Token 傳送給客戶端
  • 4, 客戶端收到 Token 以後可以把它儲存起來,比如放在 Cookie 裡或者 Local Storage 裡
  • 5, 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token
  • 6, 服務端收到請求,然後去驗證客戶端請求裡面帶著的 Token,如果驗證成功,就向客戶端返回請求的資料

總的來說就是客戶端在首次登陸以後,服務端再次接收http請求的時候,就只認token了,請求只要每次把token帶上就行了,伺服器端會攔截所有的請求,然後校驗token的合法性,合法就放行,不合法就返回401(鑑權失敗)。

Token優點與缺點

優點:

  • Token 完全由應用管理,所以它可以避開同源策略. (Cookie是不允許垮域訪問的,token不存在)
  • Token 可以避免 CSRF 攻擊(也是因為不需要cookie了)
  • Token 可以是無狀態的,可以在多個服務間共享
  • Token 支援手機端訪問(Cookie不支援手機端訪問)

伺服器只需要對瀏覽器傳來的token值進行解密,解密完成後進行使用者資料的查詢,如果查詢成功,則通過認證.所以,即時有了多臺伺服器,伺服器也只是做了token的解密和使用者資料的查詢,它不需要在服務端去保留使用者的認證資訊或者會話資訊,這就意味著基於token認證機制的應用不需要去考慮使用者在哪一臺伺服器登入了,這就為應用的擴充套件提供了便利,解決了session擴充套件性的弊端。

缺點:

  • 佔頻寬: 正常情況下token要比 session_id更大,需要消耗更多流量,擠佔更多頻寬.(不過幾乎可以忽略)
  • 效能問題: 相比於session-cookie來說,token需要服務端花費更多的時間和效能來對token進行解密驗證.其實Token相比於session-cookie來說就是一個"時間換空間"的方案.

Token與session的區別

  • 1. 使用Token,服務端不需要儲存狀態. 在session中sessionid 是一個唯一標識的字串,服務端是根據這個字串,來查詢在伺服器端保持的session,這裡面才儲存著使用者的登陸狀態。但是token本身就是一種登陸成功憑證,他是在登陸成功後根據某種規則生成的一種資訊憑證,他裡面本身就儲存著使用者的登陸狀態。伺服器端只需要根據定義的規則校驗這個token是否合法就行。

  • 2. Token不需要藉助cookie的. session-cookie是需要cookie配合的,那麼在http代理客戶端的選擇上就只有瀏覽器了,因為只有瀏覽器才會去解析請求響應頭裡面的cookie,然後每次請求再預設帶上該域名下的cookie。但是我們知道http代理客戶端不只有瀏覽器,還有原生APP等等,這個時候cookie是不起作用的,或者瀏覽器端是可以禁止cookie的(雖然可以,但是這基本上是屬於吃飽沒事幹的人乾的事),但是token 就不一樣,他是登陸請求在登陸成功後再請求響應體中返回的資訊,客戶端在收到響應的時候,可以把他存在本地的cookie,storage,或者記憶體中,然後再下一次請求的請求頭重帶上這個token就行了。簡單點來說cookie-session機制他限制了客戶端的型別,而token驗證機制豐富了客戶端型別。

  • 3. 時效性。session-cookie的sessionid實在登陸的時候生成的而且在登出事時一直不變的,在一定程度上安全就會低,而token是可以在一段時間內動態改變的。

  • 4. 可擴充套件性。token驗證本身是比較靈活的,一是token的解決方案有許多,常用的是JWT,二來我們可以基於token驗證機制,專門做一個鑑權服務,用它向多個服務的請求進行統一鑑權。

Token過期與Refresh Token

Token過期:

token是訪問特定資源的憑證,出於安全考慮,肯定是要有過期時間的。要不然一次登入便可能一直使用,那token認證還有什麼意義? token可定是有過期時間的,一般不會很長,不會超高一個小時.

Refresh Token :

為什麼需要refresh token?

如果token過期了,就要重新獲取。繼續重複第一次獲取token的過程(比如登入,掃描授權等),每一小時就必須獲取一次! 這樣做是非常不好的使用者體驗。為了解決這個問題,於是就有了refresh token.通過refresh token去重新獲取新的 token.

refresh token,也是加密字串,並且和token是相關聯的。與獲取資源的token不同,refresh token的作用僅僅是獲取新的token,因此其作用和安全性要求都較低,所以其過期時間也可以設定得長一些,可以以天為最小單位。當然如果refresh token過期了,還是需要重新登入驗證的.

JWT (JSON Web Tokens)

阮一峰老師這篇關於JWT的文章,真的是寫得非常好,也被很多人大段摘抄引用.對JWT不瞭解的同學可以直接檢視這篇文章,我這裡就不copy了.這裡我只重點總結一下.

JWT原理

JWT 的原理是,伺服器認證以後,生成一個 JSON 物件,發回給使用者.之後使用者與伺服器通訊的時候.伺服器完全只靠這個物件認定使用者身份。為了防止使用者篡改資料,伺服器在生成這個物件的時候,會加上簽名

jwt最大的特點就是: 伺服器就不儲存任何 session 資料了,也就是說,伺服器變成無狀態了,從而比較容易實現擴充套件。

JWT 的資料結構

它是一個很長的字串,中間用點(.)分隔成三個部分。

分別是:Header(頭部).Payload(負載).Signature(簽名)

  • Header : 部分是一個 JSON 物件,描述 JWT 的後設資料,例如:{ "alg": "HS256","typ": "JWT"}.alg屬性表示簽名的演算法.預設是 HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的型別(type),JWT 令牌統一寫為JWT。

頭部的 JSON 物件使用 Base64URL 演算法轉成字串。

  • Payload: 部分也是一個 JSON 物件,用來存放實際需要傳遞的資料。這個 JSON 物件也要使用 Base64URL 演算法轉成字串。

注意: JWT 預設是不加密的,任何人都可以讀到,所以不要把祕密資訊放在這個部分。

  • Signature: 部分是對前兩部分的簽名,防止資料篡改。 首先,需要指定一個金鑰(secret)。這個金鑰只有伺服器才知道,不能洩露給使用者。然後,使用 Header 裡面指定的簽名演算法(預設是 HMAC SHA256).

JWT 的幾個特點

  • (1)JWT 預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用金鑰再加密一次。
  • (2)JWT 不加密的情況下,不能將祕密資料寫入 JWT。
  • (3)JWT 不僅可以用於認證,也可以用於交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。
  • (4)JWT 的最大缺點是,由於伺服器不儲存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token的許可權。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。
  • (5)JWT 本身包含了認證資訊,一旦洩露,任何人都可以獲得該令牌的所有許可權。為了減少盜用,JWT的有效期應該設定得比較短。 對於一些比較重要的許可權,使用時應該再次對使用者進行認證。
  • (6)為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

首次登入時,後端伺服器判斷使用者賬號密碼正確之後,根據使用者id、使用者名稱、定義好的祕鑰、過期時間生成 token ,返回給前端; 前端拿到後端返回的 token ,儲存在 localStroage 和 Vuex 裡; 前端每次路由跳轉,判斷 localStroage 有無 token ,沒有則跳轉到登入頁,有則請求獲取使用者資訊,改變登入狀態; 每次請求介面,在 Axios 請求頭裡攜帶 token; 後端介面判斷請求頭有無 token,沒有或者 token 過期,返回401; 前端得到 401 狀態碼,重定向到登入頁面。

補充: 後端主動讓JWT失效的方法

前面說過JWT一旦簽發了,就不再收服務端控制了.因為它在服務端沒有記錄,是無狀態的,是它最大的優點也是最大的缺點.這樣就會造成一種不可控性.

例如:如果使用者修改了,那他之前未到期的token怎麼廢棄掉???此時服務端是沒有記錄的,它是不知道哪些未到期的token是被廢棄了的.為了解決這個問題,其實是沒有完美的方法的! 都需要後端新增狀態,只是那種方法開銷最小.

目前常見的處理方法有:

  • 1,將 token 存入 DB(如 Redis)中,失效則刪除;但增加了一個每次校驗時候都要先從 DB 中查詢 token是否存在的步驟,而且違背了 JWT 的無狀態原則(不推薦)。
  • 2,維護一個 token 黑名單,失效則加入黑名單中(用的比較多)。
  • 3,在 JWT 中增加一個版本號欄位,失效則改變該版本號。
  • 4,在服務端設定加密的 key 時,為每個使用者生成唯一的 key,失效則改變該 key。

這裡就簡單說下第二種方法:黑名單

  • 1,在簽發的jwt中payload加入一個為隨機字元的欄位token_id.
  • 2, 在服務端的分散式快取上儲存一份“groupId”黑名單。如果使用者的jwt重置密碼等需要作廢已經簽發但未過期的jwt時,就將該之前使用者的“token__id”存入到黑名單中。並分配給他一個新的“token__id”到token中.
  • 3,存入到黑名單中的“token__id”會設定一個過期時間.過期後“token_id”自動從黑名單中刪除。
  • 4,所有需要做JWT有效性校驗的伺服器,啟動時訪問分散式快取. 將黑名單下載到本地記憶體。並且訂閱分散式快取的訊息推送功能,在黑名單發生增刪的時候,接收推送訊息同步修改記憶體中的黑名單列表。
  • 5,伺服器做JWT校驗的時候,除了校驗過期時間,還要查詢記憶體中的黑名單列表。若在黑名單中,則判定該JWT為失效。

雖然黑名單還是做了分散式儲存,但黑名單本身的體積和使用頻率卻很低,所以開銷很小.

單點登入

概念

單點登入(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統。

SSO一般都需要一個獨立的認證中心(passport),子系統的登入均得通過passport,子系統本身將不參與登入操作,當一個系統成功登入以後,passport將會頒發一個令牌給各個子系統,子系統可以拿著令牌會獲取各自的受保護資源,為了減少頻繁認證,各個子系統在被passport授權以後,會建立一個區域性會話,在一定時間內可以無需再次向passport發起認證

單點登入流程

前端面試查漏補缺--(十) 前端鑑權

  • 使用者訪問系統1的受保護資源,系統1發現使用者未登入,跳轉至sso認證中心,並將自己的地址作為引數
  • sso認證中心發現使用者未登入,將使用者引導至登入頁面
  • 使用者輸入使用者名稱密碼提交登入申請
  • sso認證中心校驗使用者資訊,建立使用者與sso認證中心之間的會話,稱為全域性會話,同時建立授權令牌
  • sso認證中心帶著令牌跳轉會最初的請求地址(系統1)
  • 系統1拿到令牌,去sso認證中心校驗令牌是否有效
  • sso認證中心校驗令牌,返回有效,註冊系統1
  • 系統1使用該令牌建立與使用者的會話,稱為區域性會話,返回受保護資源
  • 使用者訪問系統2的受保護資源
  • 系統2發現使用者未登入,跳轉至sso認證中心,並將自己的地址作為引數
  • sso認證中心發現使用者已登入,跳轉回系統2的地址,並附上令牌
  • 系統2拿到令牌,去sso認證中心校驗令牌是否有效
  • sso認證中心校驗令牌,返回有效,註冊系統2
  • 系統2使用該令牌建立與使用者的區域性會話,返回受保護資源

使用者登入成功之後,會與sso認證中心及各個子系統建立會話,使用者與sso認證中心建立的會話稱為全域性會話,使用者與各個子系統建立的會話稱為區域性會話,區域性會話建立之後,使用者訪問子系統受保護資源將不再通過sso認證中心,全域性會話與區域性會話有如下約束關係

  • 區域性會話存在,全域性會話一定存在
  • 全域性會話存在,區域性會話不一定存在
  • 全域性會話銷燬,區域性會話必須銷燬

登出:

前端面試查漏補缺--(十) 前端鑑權

sso認證中心一直監聽全域性會話的狀態,一旦全域性會話銷燬,監聽器將通知所有註冊系統執行登出操作。

  • 使用者向系統1發起登出請求
  • 系統1根據使用者與系統1建立的會話id拿到令牌,向sso認證中心發起登出請求
  • sso認證中心校驗令牌有效,銷燬全域性會話,同時取出所有用此令牌註冊的系統地址
  • sso認證中心向所有註冊系統發起登出請求
  • 各註冊系統接收sso認證中心的登出請求,銷燬區域性會話
  • sso認證中心引導使用者至登入頁面

OAuth 2.0

OAuth即開發授權,其實和SSO比較像.它允許使用者授權第三方網站訪問他們儲存在另外的服務提供者上的資訊,而不需要將使用者名稱和密碼提供給第三方網站或分享他們資料的所有內容,為了保護使用者資料的安全和隱私,第三方網站訪問使用者資料前都需要顯式的向使用者徵求授權。我們常見的提供OAuth認證服務的廠商有QQ,微信,微博等。

限於篇幅,這裡推薦還是阮一峰老師的文章

感謝及參考

相關文章