基於Token的認證和基於宣告的標識
http://codelife.me/blog/2014/03/26/token-based-authentication-and-claims-based-identity/
OpenID解決跨站點的認證問題,OAuth解決跨站點的授權問題。認證和授權是密不可分的。而OpenID和OAuth這兩套協議出自兩個不同的組織,協議上有相似和重合的之處,所以想將二者整合有些難度。好在OpenID Connect作為OpenID的下一版本,在OAuth 2.0的協議基礎上進行擴充套件,很好的解決了認證和授權的統一,給開發者帶來的便利。在學習和研究OpenID Connect協議時,遇上兩個概念基於Token的認證(token based authentication)和基於宣告的標識(claims based identity)。本文就這兩個概念展開討論,為了更好的理解OpenID Connect協議的原理。
基於Cookie的認證和基於Token的認證
有兩種不同的方法實現服務端的認證
- 常見方式是基於Cookie的認證,每個請求中攜帶Cookie資訊以便於服務端識別
- 另一種新方法,基於Token的認證,在每個請求中攜帶被簽名過Token資訊傳送到服務端
相比Cookie,基於Token的認證有如下好處:
- 跨域: cookies在跨域場景表現並不好。基於Token的方法允許你向任何不同域名的伺服器傳送Ajax請求,因為你可以通過HTTP頭傳遞使用者資訊。
- 無狀態(或者 服務端可擴充套件):無須再儲存Session,由於Token已經自包含了所有的使用者資訊。
- 內容分發:你可以將所有的靜態資源(例如:指令碼,HTML,圖片等)分發到CDN服務上,你的伺服器僅僅提供API。某些CDN服務商提供了基於Token驗證的安全服務。
- 解耦:無須被繫結在一個特定的驗證方案。
- 移動支援:在移動裝置的原生平臺,使用cookie作為安全認證並不是好主意。採用基於Token的方法會簡單得多。
- 跨站點指令碼攻擊:由於沒有基於cookie技術,你不再需要考慮跨站點請求的安全性問題。
- 效能:雖然沒有做嚴格的效能測試,但是還原session所做的資料庫查詢往返的效能損耗要大於
HMACSH256
演算法驗證和解析Token。 - 基於標準:JWT(JSON Web Token)作為Token的標準已經被廣泛的接受。主流語言(.NET, Ruby, Java, Python, PHP)都有相應支援JWT標準的工具包。
JWT格式
JWT是一種緊湊的URL安全表示格式,適用於空格受限制的場景,比如HTTP授權頭部和請求引數。JWT使用JSON格式表示一組宣告,該JSON被編碼成JWS或JWE結構。
JWT是一段被base64url編碼過的字元序列(去除了尾部的“=”),並用點號分隔。
下面是一個JWT頭部的
{ |
"typ": "JWT", |
"alg": "HS256" |
} |
使用base64url編碼以後
eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9 |
下面是一個JWT的訊息體其中包含宣告集合
使用base64url編碼以後
將上面編碼過的JWS頭部和JWS訊息體使用HMAC SHA-256演算法,並結合私鑰計算得到的MAC,再經過base64url編碼獲得的字元序列成為JWS簽名
BSf1w1blYKcbxVlyOtUogUsozH2clY34xxYPd8lQIlQ |
將編碼過的頭部,訊息體和JWS簽名通過’.‘號連線起來,就獲得JWT
eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJ1c2VyX2lkIjogMX0.BSf1w1blYKcbxVlyOtUogUsozH2clY34xxYPd8lQIlQ |
關於JWT的更多技術細節,請閱讀JWT規範.
基於宣告的標識
Cookie在請求和響應之間反覆傳遞,對於無狀態的HTTP協議,在Cookie里加入一個會話ID,以標識一組請求和響應屬於同一會話。通常會話與使用者是多對一關係,也就是說一個會話只會屬於一個使用者。所以通過Cookie技術就可以標識出使用者。通常Cookie裡也會攜帶一些額外的資訊,但是考慮Cookie容易被截獲和篡改,所以Cookie裡並不適合放置使用者的基本資訊。
Token其實和Cookie類似是一段序列化字串,作為HTTP請求頭的一部分,傳送到服務端。但是Token加入了簽名機制,可以防篡改。所以Token可以包含使用者資訊傳送給不同域的應用服務作為身份標識,只要相應的應用服務能識別其有效性。
Token僅僅是某種資訊的承載形式,基於Token的認證有一個更寬泛的概念:基於宣告的標識
基於宣告的標識其實無處不在,舉個我們很熟悉的例子。
每次機場登機前過安檢時,你不能簡單地走到門口,並出示身份證。相反,必須先辦理登機手續櫃檯。如果出國,還需要出示護照。安檢人員先驗證證件頭像與你本人是否一致,然後核實您的資訊,並確認您已經購買了機票。假設一切順利,您將獲得登機牌。
登機牌上包括知道您的姓名,航班號和座位號等常規資訊。安檢人員可以從登機牌上獲得足夠的資訊以配合他們的工作。
登機牌上還有一些特殊資訊,包含在條形碼和背面的磁條裡,以證明該登機牌是由航空公司簽發的,而不是偽造的。
從本質上說,登機牌是一個由航空公司簽發的關於你的一組資訊。它表明你被允許某時登上某飛機坐在某個座位。當然,安檢人員不必深入地理解這些。他們只需驗證您的登機牌,讀取其中的資訊,然後讓你登機。
同樣值得注意的是,可能有不止一種方法獲得您的登機牌。可能去機場售票櫃檯領取,或者在家裡從航空公司網站下載並列印。安檢人員不在乎的登機牌是如何獲得的 。他們只關心登機牌是否真實的。
登機牌就是一張包含了一組宣告資訊的卡片,是Token的一種實體形式。