什么是鑒權
鑒權也叫身份認證,指驗證用戶是否有系統的訪問權限。就很像我們經常乘坐動車的票據(對應的標識,一定的時間范圍)。
認證方式
接下來介紹幾種我們工作中通常用到的認證方式。
Session-Cookie 認證
利用服務端的 Session(會話)和瀏覽器(客戶端)的 Cookie 來實現的前后端通信認證模式。
來源
由於 HTTP 請求時是無狀態的,服務端正常情況下無法得知請求發送者的身份。這個時候如果我們要記錄狀態,就需要在服務端創建會話,將相同客戶端的請求都維護在各自的會話記錄中,每當請求到達服務端時,先校驗請求中的用戶標識是否存在於 Session 中,如果有則表示已經認證成功,否則表示認證失敗。
流程
實踐
boss(我們的一個產品) 這邊 Session ID 存在數據庫里面,在 Memcached 里面做緩存。客戶端每次調用接口的時候會通過 response headers 里面的 Set-Cookie 更新過期時間(boss 這邊設置的是 6 個小時),這樣做的作用是防止你在做一些復雜操作的時候,cookie 突然過期。
⚠️整個過程是比較重的,因為每次的接口調用都得更新過期時間。
優缺點
優點:
- 簡單易用,瀏覽器會自動帶上
缺點:
- 脫離瀏覽器沒法用,比如原生應用
關於 Cookie 的安全問題
Cookie 屬性:
提高安全性的辦法
-
Expires/Max-Age 設置合理過期時間
-
HttpOnly 設置為 true
-
Secure 設置為 true(使用 https)
Token 認證
來源
負載均衡多服務器的情況,不好確認當前用戶是否登錄,因為多服務器不共享 Session。這個問題也可以將 Session 存在一個服務器中來解決,但是就不能完全達到負載均衡的效果。
Token 和 Session-Cookie 認證方式中的 Session ID 不同,並非只是一個標識符。Token 一般會包含用戶的相關信息,通過驗證 Token 不僅可以完成身份校驗,還可以獲取預設的信息。
客戶端可以將 token 存放於 localStroage 等容器中。客戶端每次訪問都傳遞 token,服務端解密 token,服務端就不需要存儲 Session 占用存儲空間,就很好的解決負載均衡多服務器的問題了。
流程
實踐
平常用的最多的就是 JSON Web Token(JWT),也是目前最流行的跨域身份驗證解決方案。
JWT 組成:頭部. 載荷. 簽名
頭部和載荷用 base64 編碼
簽名計算:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload) , secret)
使用方法:
Authorization: Bearer <token>
但是 JWT 有個大缺點是服務器不保存會話狀態,所以在使用期間不可能取消令牌或更改令牌的權限。也就是說,一旦 JWT 簽發,在有效期內將會一直有效。
⚠️載荷的內容任何人都可以讀到,不要放入敏感信息
jwt 存儲位置的爭論:我覺得如果存儲信息多,天然防止 csrf 的話,放到 localStorage 或者 sessionStoraged 都行。
除了 JWT 可以提升 token 的安全性,Refresh token 也可以。
業務接口用來鑒權的 token,我們稱之為 access token。越是權限敏感的業務,我們越希望 access token 有效期足夠短,以避免被盜用。但是過短的有效期會造成 access token 經常過期,過期后怎么辦呢?
一種辦法是,讓用戶重新登錄獲取新 token,顯然不夠友好,要知道有的 access token 過期時間可能只有幾分鍾。
另外一種辦法是,再來一個 token,一個專門生成 access token 的 token,我們稱為 refresh token。
refresh token 的過期時間一般比較長,比如 6 個小時,access token 的過期時間比較短,比如 10 分鍾。我們在實際業務中,api 調用時只傳遞 access token 進行鑒權。如果 access token 過期,則使用 refresh token 去授權服務器更新 access token。最終 refresh token 也過期了,這時候用戶就得重新登陸了。
優缺點
優點:
- 輕量,服務端不用存儲,移動端可用
缺點:
- 一旦派發出去,失效之前都是有效的(雖然可以解決,但是就類似於 Session 機制了)
單點登錄
來源
但當我們業務線越來越多,就會有更多業務系統分散到不同域名下,就需要「一次登錄,全線通用」的能力,叫做「單點登錄」。
流程
對瀏覽器來說,SSO 域下返回的數據要怎么存,才能在訪問 A 的時候帶上?這就需要也只能由 A 提供 A 域下存儲憑證的能力。
實踐
OIDC
-
OIDC 登陸點擊,重定向到登錄的 OpenID 網站
-
輸入用戶名密碼,如果驗證成功。則會重定向到登陸回調(之前設置好的地址)
-
回調地址里面有個 code 參數,code 驗證正確后,下發 sk,boss 系統登陸成功
-
前端通過添加 iframe 的方式輪詢 authing 鏈接實現單點登出
關於 OIDC
OIDC 是一個 OAuth2 上層的簡單身份層協議。它允許客戶端驗證用戶的身份並獲取基本的用戶配置信息。OIDC 使用 JSON Web Token(JWT)作為信息返回,通過符合 OAuth2 的流程來獲取。
關於 OAuth2
OAuth2 最終目的是為第三方應用頒發一個有時效性的令牌 token。使得第三方應用能夠通過該令牌獲取相關的資源。當你想要登錄某個論壇,但沒有賬號,而這個論壇接入了如 QQ、Facebook 等登錄功能,在你使用 QQ 登錄的過程中就使用的 OAuth 2.0 協議。
-
Client 請求 Resource Owner 的授權。授權請求可以直接向 Resource Owner 請求,也可以通過 Authorization Server 間接的進行。
-
Client 獲得授權許可。
-
Client 向 Authorization Server 請求訪問令牌。
-
Authorization Server 驗證授權許可,如果有效則頒發訪問令牌。
-
Client 通過訪問令牌從 Resource Server 請求受保護資源。
-
Resource Server 驗證訪問令牌,有效則響應請求。
關於 LDAP
LDAP (Light Directory Access Portocol),中文名輕量目錄訪問協議,是一個開放、廣泛被使用的工業標准。比如我們的 Jira、Confluence、Yapi。
但是 LDAP 並不能做到單點登錄 SSO,只是可以用同樣的用戶名和密碼可以登陸不同的系統,但達不到一次登陸之后可以訪問多個系統。
Others 認證方式
2FA(雙因素認證)
線上的 boss 必須開啟二次認證,會生成一個二維碼,那個二維碼就是一個 SecretKey,通過 CryptoJS.HmacSHA1(默認算法),每次會計算出一個 6 位(默認長度)隨機數。計算公式為
⚠️因為默認是 30s 內有效,所以用戶手機時間要比較准確
Google 驗證器
密鑰二維碼
http://otpauth//totp/ 青雲 QingCloud 雲計算管理平台:deanchen@yunify.com?secret=xxx&issuer = 青雲 QingCloud 雲計算管理平台
xxx: 大寫的字母數字 16 位
使用 base32 的解碼密鑰
密鑰必須大寫沒有空格
獲取 Unix 時間戳
let epoch = Math.round(new Date().getTime() / 1000.0);if (localStorage.offset) {
epoch = epoch + Number(localStorage.offset);
}
counter = Math.floor(epoch / period); // period 一般為30 這個失效實現的想法太棒了
計算簽名
const time = this.leftpad(this.dec2hex(counter), 16, "0");const key = this.base32tohex(secret)
CryptoJS.HmacSHA1(
CryptoJS.enc.Hex.parse(time),
CryptoJS.enc.Hex.parse(key)
)
得出結果
const len = 6;const result = otp.substr(otp.length - len, len).toString() // 默認取的最后6位
作者
Dean 青雲科技高級工程師
本文由博客一文多發平台 OpenWrite 發布!