【Go Web 開發】用戶認證

接下來的幾篇文章,我們將看看如何對 API 服務的請求進行認證,以便我們準確地知道一個請求來自於哪個用戶。

記住:認證是關於確認用戶是誰,而授權是檢查用戶是否有權做某些事情。

我們將:

認證機制

在開始編寫代碼之前,我們先來討論一下如何對 API 的請求進行身份驗證,並找出請求來自於哪個用戶。

選擇一種高級的 API 身份驗證方法可能很困難——有許多不同的選項,而且並不能立即清楚哪種方法適合您的項目。因此,在本節中,我們將從宏觀層面討論一些最常見的方法,討論它們的優缺點,並在結束時介紹哪些情況適合使用哪種方法。

具體來說,我們將對比以下 5 種認證方式:

提示: 關於我們在本節中描述的所有身份驗證方法,前提是你的 API 只通過 HTTPS 與客戶端通信。

HTTP 基本認證

確定誰向 API 服務發出請求的最簡單方法可能是使用 HTTP 基本身份驗證。使用此方法,客戶端包含一個 Authorization 請求頭,每個請求都包含它們的憑證。憑證採用用戶名: 密碼和 base-64 編碼的格式。因此,例如,要驗證 alice@example.com:pa55word,客戶端將發送以下報頭:

Authorization: Basic YWxpY2VAZXhhbXBsZS5jb206cGE1NXdvcmQ=

服務端可以使用 Go 的 Request.BasicAuth() 方法從請求頭中提取憑證信息,在繼續處理請求之前,驗證憑證信息是否正確。HTTP 基本身份驗證的一大優點是對客戶端來說非常簡單。用戶只需要爲每個請求發送相同的頭信息即可——大多數編程語言、web 瀏覽器以及 curl 和 wget 等工具都支持 HTTP 基本身份驗證。

當您的 API 沒有用戶管理,但您想要一種快速、簡單的方式來限制對 API 的訪問或保護它不被窺探時,這種方法通常很有用。

對於有用戶賬號的 APIs 服務,尤其是使用密碼哈希處理的,HTTP 基本認證不是很合適。將客戶端提供的密碼和密碼的哈希值做對比是一個代價比較高的操作,而使用 HTTP 基本認證需要每個請求都做對比處理。這將爲 API 服務器帶來大量額外的工作,並增加響應延遲。但即便如此,如果 API 的流量非常低,響應速度對您來說並不重要,那麼基本身份驗證仍然是一個不錯的選擇。

token 認證

token 認證的工作原理:

1、客戶端發送請求到 API 服務時包含認證信息 (一般是用戶名或郵箱以及密碼)。

2、API 服務驗證認證信息是否正確,並生成代表用戶身份的 bearer token(不記名令牌) 返回給客戶端。token 在一段時間後過期,在此之後,用戶將需要重新提交他們的憑證以獲得一個新的 token。

3、對於後續的 API 請求,客戶端將拿到的 token 放在 Authorization 請求頭中,如下所示:

 Authorization: Bearer <token>

4、當 API 服務接收到這個請求時,它檢查 token 是否過期,並檢 token 值以確定用戶是誰。

對 API 服務的用戶密碼做了哈希處理(類似本系列文章前面所述),這種方法比基本認證更合適,因爲耗時的密碼校驗只需要定期執行即可 - 第一次創建 token 和 token 過期才需要校驗密碼。

這種方法的缺點是對客戶端來說管理 token 可能比較複雜 - 客戶端需要實現必要的 token 緩存邏輯,監控和管理 token 的過期,定期生成新的 token。

我們可以將 token 認證進一步分成兩類:有狀態的和無狀態的認證。它們的優點和缺點是非常不同的,所以讓我們分別討論。

有狀態 token 認證

在有狀態 token 認證方法中,token 的值是一個高熵 (隨機性高) 密碼安全的隨機字符串。這個 token 或其哈希值以及用戶 ID 和令牌的失效時間,都是存儲在服務端的數據庫中。

當客戶端在請求當中攜帶 token,API 服務查詢數據庫中 token 檢查是否過期,並檢索對應的用戶 ID 來確認請求來自哪個用戶。這種認證方式的優點是服務端控制 tokens,通過從數據庫中刪除 token 或將它們標記爲過期,很直接地實現一個用戶對應一個 token。

從理論上講,它也很簡單和健壯——通過 “不可猜測的”token 來提供安全性,這就是爲什麼使用一個高熵加密安全的隨機 token 值很重要。

那缺點是什麼呢?

除了管理 token 給客戶端帶來複雜性之外,對於這種方法,我們很難找到太多可以批評的地方。也許它需要數據庫查詢算一個缺點——但在大多數情況下,都需要進行數據庫查找,以檢查用戶的激活狀態或檢索有關他們的額外信息。

無狀態 token 認證

與有狀態認證不同,無狀態 token 將用戶 ID 和過期時間都編碼到 token 中。對 token 進行加密簽名以防止篡改,並且 (在某些情況下) 對 token 進行加密以防止讀取其內容。

有幾種創建無狀態 token 的技術。將信息加密到一個 JWT(JSON Web Token) 中可能是最常見的方法,但 PASETO,Branca 和 nacl/secretbox 也是不錯的選擇。儘管這幾種技術實現細節不同,在認證方面的主要優點和缺點是相似的。

使用無狀態 token 進行身份驗證的主要優點是,對 token 進行編碼和解碼的工作可以在內存中完成,標識用戶所需的所有信息都包含在令牌本身中。不需要執行數據庫查詢來找出請求來自哪個用戶。

無狀態 token 的主要缺點是,一旦它們被髮行,就不容易被撤銷。

在緊急情況下,您可以通過更改用於 token 簽名的密碼來有效地撤銷所有令牌 (迫使所有用戶重新認證),或者另一種解決方法是在數據庫中維護一個被撤銷 token 列表(儘管這違背 token 的“無狀態” 特性)。

提示:通常您應該避免在無狀態 token 中存儲額外的信息,例如用戶的激活狀態或權限,並將其作爲授權檢查的基礎。在 token 的生命週期內,編碼到其中的信息可能會過時,並且與系統中的實際數據不同步——依賴舊數據進行授權檢查很容易導致用戶的意外行爲和各種安全問題。

最後,尤其是 JWT,它們高度可配置意味着可能容易出錯。JWT 庫中的關鍵漏洞 [https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/] 和 JWT 安全最佳實踐 [https://curity.io/resources/learn/jwt-best-practices/] 兩篇文章很好地介紹了你需要注意的細節。由於這些缺點,無狀態 token(特別是 JWT)通常不是大多數 API 應用程序中管理身份驗證的最佳選擇。

但是它們在需要委託身份驗證的場景中非常有用——創建身份驗證 token 的應用程序與使用它的應用程序不同,而且這些應用程序不共享任何狀態 (這意味着不能使用有狀態令牌)。例如,如果你正在構建一個具有微服務風格架構的系統,那麼由“身份驗證” 服務創建的無狀態令牌隨後可以傳遞給其他服務來識別用戶。

API key 認證

API-key 認證的思想是用戶有一個和賬號關聯的不過期 “key“。該 key 是一個高墒安全的隨機字符串以及 key 的哈希值(SHA256 或 SHA512) 需要和用戶 ID 一起存在數據庫中。

用戶在每個 API 請求頭中都帶上他們的 key,如下所示:

 Authorization: Key <key>

API 服務接收到請求後,可以快速生成 key 的哈希值,根據哈希值查詢數據庫中對應的用戶 ID。從概念上講,這與有狀態 token 方法沒有什麼不同——主要的區別是 key 是永久的,而不是臨時 token。

一方面客戶端可以使用同一個 key 來發起每個請求,不需要寫代碼管理 token 及其過期時間。另一方面,用戶現在有兩個長期存在的安全信息需要管理,這兩個安全信息可能會危及他們的帳戶,分別是:密碼和 API Key。

支持 API keys 對服務端來說也增加了額外的複雜性,需要一種方式來生成 API key 如果丟失了或者泄漏的情況,而且你可能希望一個用戶有多個 API key,用戶可以根據不同用途使用不同 key。

同樣重要的是,API key 本身應該只通過安全通道與用戶通信,並且應該像對待用戶密碼一樣對待它們。

OAuth2.0/OpenID Connect

另一種方法是使用 OAuth2.0 來驗證。使用這種方法,用戶的信息是保存在第三方身份認證提供者那裏,例如 Google 或 Facebook 而不是自己的服務中。

關於 OAuth2.0 首先要提到的是它不是一種認證協議,你不能用它來認證用戶。關於 OAth2.0 oauth.net[https://oauth.net/] 有相關的文章介紹我強力推薦。

如果你想使用第三方服務實現認證,應該使用 OpenID Connect(它是基於 OAuth2.0 構建的)。

這裏 [https://connect2id.com/learn/openid-connect] 有一個關於 OpenID Connect 的全面描述,它的工作原理如下所示:

1、當你要認證一個請求時,您將用戶重定向到由身份提供者託管的 “身份驗證和同意” 表單。

2、如果用戶同意,那麼身份提供者將向 API 發送授權代碼。

3、然後,您的 API 將授權碼發送到認證提供者提供的另一個接口。它們驗證授權代碼,如果授權碼有效,它們將向您發送包含 ID token 的 JSON 響應。

4、ID token 本身是 JWT,你需要校驗、解碼獲取用戶的實際信息:包括用戶 email 地址、用戶名,出生日期,時區等。

5、你知道用戶是誰,就可以實現一個有狀態或無狀態 token 認證,不需要每個請求都走上面的流程。

和其他認證方法一樣,使用 OpenID Connect 也有利有弊。最大的優點就是你不需要存儲用戶信息或密碼。最大缺點就是實現複雜,儘管有一些輔助工具例如 coreos/go-oidc 簡化了複雜性並提供了簡單的 OpenID connect 功能接口。

還需要指出的是使用 OpenID connect 需要用戶有一個三方服務賬號,而 “認證和同意” 步驟需要通過瀏覽器進行人機交互——如果你的 API 是一個網站的後端,這可能是沒問題,但如果它是一個“獨立的”API 與其他計算機程序作爲客戶端,就不理想了。

該使用哪種認證方法

對於選擇哪種身份驗證方法最適合您的 API,很難給出全面的指導。就像編程中的大多數事情一樣,不同的工具適用於不同的工作。

但簡單粗略的經驗法則是:

在後面的文章中,我們將使用有狀態認證 token 方式。在我們前面的編碼中,作爲激活 token 工作的一部分,我們已經爲 token 構建了許多必要的邏輯。

注意:儘管我不推薦 JWT,除非你需要某種代理認證,我知道它們在開發者社區中有很多分享,而且經常被更廣泛地使用。因此,在後面的文章中會介紹如何在 Greenlight API 中使用 JWT 實現無狀態認證 token。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/zaJuhaHSBI-YQVx_mhEX2A