先後端鑑權二三事

DevUI是一支兼具設計視角和工程視角的團隊,服務於華爲雲 DevCloud平臺和華爲內部數箇中後臺系統,服務於設計師和前端工程師。
官方網站: devui.design
Ng組件庫: ng-devui(歡迎Star)
官方交流羣:添加DevUI小助手(微信號:devui-official)進羣
DevUIHelper插件:DevUIHelper-LSP(歡迎Star)

引言

先後端鑑權是一個很大的話題,不一樣組織的鑑權方式各不相同,甚至對同一協議的業務實現也可能相去甚遠。本文嘗試從認證與受權兩個維度來描述標題中的鑑權,大部分篇幅仍是偏認證。文章主要包含三部份內容:區分認證與受權、常見的認證及受權方式和企業應用中常見的單點登陸(SSO)方案。html

1 認證與受權

首先,咱們來簡單看一下認證與受權,並理清楚二者之間的區別。前端

認證(Authentication)

認證涉及一方應用和一方用戶,用於描述用戶在該應用下的身份。認證能夠簡單理解爲登陸,以此確認你是一個合法的用戶。好比說掘金必需要登陸才能點贊、收藏。git

受權(Authorisation)

受權涉及兩方應用和一方用戶,用於描述第三方應用有哪些操做權限。github

帶入場景區分認證與受權

咱們分別舉三個例子來講明三種狀況讓你們對認證和受權的關係有更好的理解:只認證不受權、即認證又受權、不認證只受權。web

(1)只認證不受權

上面提到的使用掘金帳號登陸掘金就是隻認證不受權的場景,此時掘金只知道你是哪一個用戶,可是不涉及到受權的操做。ajax

(2)即認證又受權

一樣是登陸掘金,咱們能夠不使用在掘金註冊的帳號和密碼登陸,而選擇第三方應用登陸,好比說github 帳號。此時會彈出github 的登陸頁面,若是你在此頁面輸入帳號和密碼進行登陸,則至關於默認受權給掘金獲取你的github 的頭像和帳號名。在這個過程當中即完成了認證(合法用戶)又完成了受權(你容許掘金從github 獲取你的信息)。json

(3)不認證只受權

以某外賣小程序爲例,在你第一次進入外賣小程序的時候小程序會彈框請求獲取你的我的信息,此時至關於上面提到的即認證又受權。你贊成之後就至關於使用微信帳號登陸,可是此時外賣小程序獲取到的你的信息不包括你的手機號。當你要下單點擊提交的時候,小程序再次發起請求,要獲取你微信綁定的手機號,此時發生的動做就是不認證只受權。小程序

2 有哪些經常使用的認證和受權方式?

一旦涉及認證,必需要考慮的一個問題就是狀態管理。所謂的狀態管理就是說咱們在一個網站進行登陸以後的一段時間裏,不但願每次訪問它都須要從新登陸,因此應用開發者必需要考慮怎麼樣保持用戶的登陸狀態以及決定什麼時候失效。而這個過程須要先後端通力合做來完成。下面介紹幾種常見的認證和受權方式。c#

Session-Cookie 認證

(1)流程

Session-Cookie 的認證流程以下:用戶先使用用戶名和密碼登陸,登陸完成後後端將用戶信息存在session 中,把sessionId 寫到前端的cookie 中,後面每次操做帶着cookie 去後端,只要後端判斷sessionId 沒問題且沒過時就不須要再次登陸。segmentfault

使用這種方式進行認證,開發者可能面臨的主要問題以下:

  • cookie 安全性問題,攻擊者能夠經過xss 獲取cookie 中的sessinId,使用 httpOnly 在必定程度上提升安全性
  • cookie 不能跨域傳輸
  • session 存儲在服務器中,因此session 過多會耗費較大服務器資源
  • 分佈式下session 共享問題

Token 認證

與上面的Session-Cookie 機制不一樣的地方在於,基於token 的用戶認證是一種服務端無狀態的認證方式,服務端能夠不用存放token 數據,可是服務器能夠驗證token 的合法性和有效性。使用token 進行認證的方式這裏主要介紹兩種:SAML 和JWT.

SAML (Security Assertion Markup Language)

SAML 的流程以下:

  • 未登陸的用戶經過瀏覽器訪問資源網站(Service Provider,簡稱SP)
  • SP 發現用戶未登陸,將頁面重定向至IdP(Identity Provider)
  • IdP 驗證請求無誤後,提供表單讓用戶進行登陸
  • 用戶登陸成功後,IdP 生成併發送SAML token (一個很大的XML對象) 給SP
  • SP 對token 進行驗證,解析獲取用戶信息,容許用戶訪問相關資源

針對上面的流程補充兩點信息:

(1)SP 是如何驗證token 的合法性?

好比是否有可能token 在IDP 到SP 的過程當中被人劫持並修改了內容?

答案是:沒有可能。由於IDP 返回給SP 的token 使用IDP 的私鑰進行了簽名,而經過私鑰簽名後的信息能夠經過對應的公鑰進行驗證。

(2)SP 如何判斷token 是否過時?

SAML token 攜帶了token 過時時間的信息。

(3)生成的SAML token 是託管在SP 仍是前端?

若是是託管在SP,那麼又要引入session 機制,若是託管在前端,那麼前端須要存儲而且每次傳遞SAML token,可是SAML token 大小又比較大,耗費傳輸資源。

答案是:均可以。放在前端的話須要前端經過單獨的ajax 請求獲取token 並存儲在localStorage 或者其餘的本地存儲中。若是是託管在SP,那麼就像上面說的,引入session,前端只掌握sessionId,這樣的話token 機制其實就退化成了上面提到的session-cookie 機制。

JWT(JSON Web Token)

關於JWT 的文章有不少,這裏再也不贅述,相關信息能夠參考阮一峯老師的入門文章:JSON Web Token 入門教程(預計閱讀時間:2mins)

簡言之,JWT 就是一種在用戶登陸後生成token 並把token 放在前端,後端不須要維護用戶的狀態信息可是能夠驗證token 有效性的認證及狀態管理方式。

文章裏已經有的內容這裏不過多探討,想聊一聊的是在此基礎之上延伸出的一個問題:

JWT 用於簽名和驗證簽名的secret 對於全部人來講都是同樣的嗎?

若是同樣的則存在比較大的安全隱患,一旦泄露,全部JWT 均可能會被破解。若是不同,那麼一樣須要在服務器端維護每個人對應的secret 信息,這樣的話和服務器端維護session 信息又有什麼區別呢?

從JWT官方Introduction 的介紹文檔中看到這樣一句話:

The signature is used to verify the message wasn't changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.

也就是說,secret 能夠用服務器私鑰。若是這樣的話,對於全部用戶都是同樣的。若是服務器私鑰丟了,那全部的安全都無從談起,因此JWT 就是假設這個私鑰不會丟。固然按個人理解,開發者也能夠爲每一個用戶設置一個單獨的secret,這就必需要面臨上面提到的複雜性問題了。

關於JWT 和SAML 的對比,有一張頗有意思的圖片

OAuth 受權

OAuth 的設計本意更傾向於受權而不是認證,因此這一小節的標題寫的是受權,可是其實在受權的同時也已經完成了認證。

本文偏向於認證,OAuth 在這裏不過多討論,更多OAuth 內容能夠參考這篇:理解OAuth 2.0

3 SSO 與CAS

接下來咱們探討一個企業應必定繞不過的課題:單點登陸。

舉例來講,華爲雲下有若干雲服務。包含項目管理、代碼託管、代碼檢查、流水線、編譯構建、部署、自動化測試等衆多微服務的DevCloud(軟件開發雲) 正是其中之一,用戶若是在使用任意一個服務沒有登陸的時候均可以去同一個地方進行登陸認證,登陸以後的一段時間內能夠無需登陸訪問全部其餘服務。

在單點登陸領域,CAS(Central Authentication Service,中文名是中央認證服務) 是一個被高頻使用的解決方案。所以,這裏介紹一下利用CAS 實現SSO。而CAS 的具體實現又能夠依賴不少種協議,好比OpenID、OAuth、SAML 等,這裏重點介紹一下CAS 協議。

CAS 協議中的幾個重要概念

簡單介紹一下CAS 協議中的幾個重要概念,一開始看概念可能很模糊,能夠先過一遍,再結合下面的流程圖來理解。

  • CAS Server:用於認證的中心服務器
  • CAS Clients:保護CAS 應用,一旦有未認證的用戶訪問,重定向至CAS Server 進行認證
  • TGT & TGC:用戶認證以後,CAS Server 回生成一個包含用戶信息的TGT (Ticket Granting Ticket) 並向瀏覽器寫一個cookie(TGC,Ticket Granting Cookie),有啥用後面流程會講到
  • ST:在url 上做爲參數傳輸的ticket,受保護應用能夠憑藉這個ticket 去CAS Server 確認用戶的認證是否合法

CAS 協議核心流程

介紹完概念,結合官方給出的流程圖(先耐心地把圖看一遍再看後面的流程解析效果更佳),對每一步進行詳細的拆解,並點出幾個可能會讓人感到疑惑的問題。

① 用戶經過瀏覽器訪問受保護應用(如下簡稱app_1)首頁

② app_1 側的CAS Client 經過檢測session 的方式察覺到到用戶未進行過認證,將用戶重定向(第一次重定向)到CAS Server,url 上攜帶的參數service 包含了app_1 的訪問地址

③ CAS Server 檢測到用戶瀏覽器沒有TGC,提供表單讓用戶登陸,用戶登陸成功後,CAS Server 生成包含用戶信息的TGT,並寫TGC 到用戶瀏覽器

  • TGC 跟TGT 相關聯,是用戶瀏覽器直接向CAS Serv er 獲取ST 的票據,若是TGC 有效,用戶就不須要完成表單信息填寫的步驟直接實現登陸
  • TGC 的過時策略是這樣設置的,若是用戶一直沒有頁面操做和後臺接口請求,那麼默認2 小時過時,若是一直有操做,默認8 小時過時,開發者能夠在cas.properties 中對這兩個過時時間進行修改,通常的應用中不會配置這麼長的過時時間
# most-recently-used expiration policy
cas.ticket.tgt.timeout.maxTimeToLiveInSeconds=7200
# hard timeout policy
cas.ticket.tgt.timeout.hard.maxTimeToLiveInSeconds=28000

④ CAS Server 把瀏覽器重定向(第二次重定向)回app_1 首頁,此時重定向的url 攜帶了ST

⑤ app_1 再次接收到用戶瀏覽器的訪問,把上一步url 參數中的ST 拿出來,憑着ST 去CAS Server 確認當前用戶是否已經完成認證,CAS Server 給出確定回覆之後,app_1 拿掉url 上的ST 重定向(第三次重定向)瀏覽器至app_1 首頁

  • app_1(CAS Client)憑藉ST 去向CAS Server 確認當前用戶認證狀態的同時獲取了包含用戶信息在內的額外信息
  • 把這些額外信息寫到session 裏並把sessionId 返回給前端,那麼前端下一次訪問的時候直接判斷session 是否有效就能夠了

⑥ 用戶端瀏覽器去訪問同一認證體系下的app_2 首頁

⑦ 同第②步,app_2 側的CAS Client 經過檢測session 的方式察覺到到用戶未進行過認證,將用戶重定向到CAS Server,url 上攜帶的參數service 包含了app_2 的訪問地址

⑧ CAS Server 檢測到用戶瀏覽器的TGC,找到對應的TGT,經驗證是合法的,此處呼應了第③步的TGC

⑨ 同第④步,CAS Server 把瀏覽器重定向回app_2 首頁,此時重定向的url 攜帶了ST

⑩ 同第⑤步,app_2 再次接收到用戶瀏覽器的訪問,把上一步url 參數中的ST 拿出來,憑着ST 去CAS Server 確認當前用戶是否已經完成認證,CAS Server 給出確定回覆之後,app_2 拿掉url 上的ST 重定向瀏覽器至app_2 首頁

關於CAS 流程中的幾個問題

(1)如何避免sessionId 衝突?

業務服務器(咱們不妨把它當作跟先後文中提到的CAS Client是一個東西)經過在服務端寫session 而且把sessionId 傳回給前端保存的方式,保證用戶登陸的一段時間內不須要再次登陸。那麼如何保證使用同一單點認證的各個子服務(下文以服務a 和服務b 來舉例描述)的的sessionId 不衝突?固然這個問題的前提是服務a 和服務b 沒有使用共享session 的狀況。

若是服務a 和服務b 使用了共享session,那麼他們的sessionId 確定是一致的,即二者的CAS Client 在上述流程②中檢測的session 是一致的。此時若是用戶已經在服務a 登陸,那麼能夠直接訪問服務b,由於在第②步的時候登陸狀態就已經驗證經過。

若是服務a 和服務b 沒有使用共享session,那麼用戶在服務a 登陸以後,再去訪問服務b,要走上面流程中的第⑧步才能夠確認用戶是登陸過的,此時,用戶仍然不須要登陸就能夠訪問,可是驗證流程相比於共享session 要長不少,若是你去觀察network 中的全部請求,也會發如今這個過程當中多了幾個302。此處要討論的問題偏偏是在這種狀況下a 和b 如何避免sessionId 衝突。

一旦發生衝突,就會致使用戶在a 和b 之間切換的時候,雙方的CAS Client 須要不斷地去CAS Server 確認並刷新session。這一段的描述若是不太好理解,能夠往上翻一翻再看一遍上面流程圖中的【First Access To Second Application】部分。其實要避免衝突也很簡單,即a 和b 各自在本身寫入前端cookie 的key 上加入服務名做爲前綴,好比分別寫成a_sessionId 和b_sessionId。

(2)假設a 與b 使用一樣的單點登陸認證Server,有沒有可能出現a 應用登陸過時,b 應用沒有過時的狀況?

接着上面的問題進行討論,a 和b 在不使用共享session 的狀況下,有沒有可能出現這樣一個狀況:a 應用的認證狀態過時了(session 和TGC 都無效)而b 應用仍沒有過時(session 或TGC 至少存在一個有效)?

答案是:不會。在業務實現中,CAS Client 會按期和CAS Server 進行通訊,若是用戶一直在操做,那麼CAS Server 就會相應延長TGC 的過時時間,最終對於a 和b 來講,TGC 的過時時間必定是相同的。因此哪怕兩邊的session 設置過時時長不一致,認證狀態最多走到CAS Server 處經過TGC 的檢測就能完成,而不會出現a 須要登陸,b 不須要登陸的狀況。

總結

本文首先探討了認證與受權的區別,並列舉了幾種常見的認證與受權方式。而後重點介紹了一下使用CAS 協議實現單點登陸的流程與問題。最後,補充一點。華爲雲DevCldoud 的CAS Client 正是參考標準的CAS 協議實現,感興趣的同窗能夠在這裏註冊一個帳號,而後打開F12 使用帳號登陸觀察全部的網絡請求並分析一下CAS 業務實現的完整流程。

加入咱們

咱們是DevUI團隊,歡迎來這裏和咱們一塊兒打造優雅高效的人機設計/研發體系。招聘郵箱:muyang2@huawei.com。


文/DevUI 少東

參考文章

往期文章推薦

《好用到飛起!VSCode插件DevUIHelper設計開發全攻略(一)》

《Web界面深色模式和主題化開發》

《手把手教你搭建一個灰度發佈環境》

相關文章
相關標籤/搜索