JWT

OAuth2 現在已經很多的網站都有實作了,像是 Twitter, Google, Facebook, Github 等網站本身都有支援 OAuth2 的驗證與授權機制

最近因為專案需求,特地去研究了一下有關 Web API 做驗證授權的機制,看到網路上有很多的範例,在講解 AngularJS SPA 的登入作法,想說可以把類似的機制放到 RESTful Service 的驗證上面

以下是 JWT 的規格文件 (老實說,這份規格並不好懂.....我看了很久.....其實還是一知半解)
JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants

後來有找到,現在的 Owin.Security Package 中,有一個 Microsoft.Owin.Security.Jwt 的 Nuget package,就是在實作相關的程式,所以找了程式範例,自己下來實作。
如果沒有特別的需求,其實用 Owin 來做 JWT 的使用與驗證其實還蠻簡單的。

JWT 簡介

先大概解釋一下什麼是 JWT
根據 jwt.io 這個網站的首頁,就說到 JWT (JSON Web Token) 的主要意義是什麼?

JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS). IETF

salesforce 有一份中文解釋

JSON Web 權杖 (JWT) 是 JSON 型安全性權杖加密,能夠使身分和安全性資訊在安全性網域之間共用。 OAuth 2.0 JWT 承載者權杖流程定義了當用戶端希望運用先前授權時,使用 JWT 以從 Salesforce 要求 OAuth 存取權杖的方式。適用 JWT 的數位簽署會提供已授權應用程式的驗證。

JWT 角色

  • Token Provider - 負責發出 JWT,並且事先要先提供給 Web API 服務端 Signature,用來確認 JWT 有效性
  • Web API 用戶端 - 提出 API 呼叫的使用者端,呼叫服務端之前,要先跟 Token Provider 要求 JWT。
  • Web API 服務端 - 先跟 Token Provider 取得 Signature,接收到 JWT 之後,透過 Signature 檢查 JWT 有效性

其中,Token Provider 並不見得一定要是另外的應用服務,若用戶端以及服務端先談好,其實用戶端自己可以簽發 JWT,只要按照一定的方式做就可以

Why JWT?

我自己認為,其實 JWT 就是用戶端想一個方式,產生一組 JWT,把使用者相關的身分資料都以 JSON 的格式組合,然後做一個基本的 Sign,這個 Sign 的方式,會是與 Web API Server 事先談好。

Web API Server 端在接收到 JWT 之後,只需要以事前約定好的 Signature 做解密,確認 JWT 並未被 Third-Party 竄改,如此便可以信任 Token Provider 所發出的 JWT,並將 JWT 內的資料轉換成 ClaimsIdentity 存入本地端,讓 Web API 可以使用

這個方式的好處是:

  • 服務端在驗證的時候,並不需要與 Token Provider 連接確認 JWT 的有效性
  • 避免第三方竄改 JWT 的內容
  • 可以傳遞的資料相對比較多,因為是 JSON 格式
  • server 可以達到 stateless 的目標 (RESTful)
  • server 的 scalability 可以很大,因為不需要有 mapping table,隻需要有 signature 就可以在 N 台 server 上解開
  • 避免掉過去一些 cookie 的安全性問題,像是 CSRF
  • 效能可能有點不一樣:如果你的 session mapping table 是存在 database,那就看看 access db 的時間和解 sha-256 的時間哪個比較快

JWT 內容

整個 JWT 分成三個部分,JOSE Header,JWT Claims Set,Sign,這是三個 Base64 的字串,用 . 組合起來

第一個部分是 JOSE Header,描述了這份 JWT 是採用哪個演算法做 hash,像是下面的範例就說明了這個 JWT 是用 HMAC-256 的加密法

1
{
2
  "typ" : "JWT",
3
  "alg" : "HS256"
4
}

加密後大概是像下面這樣

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9

第二個部分是本文,JWT Clams Set則是包含你要傳的資訊(JSON format)

  • aud: 簽發 JWT 的單位 (Token Provider)
  • exp: 到期時間(單位是秒,所以getTime 是 ms 要除 1000)
  • iat: 申請時間
  • iss: 申請者,一般用來識別用戶端

範例如下

{
  "iss": "joe",
  "exp": 1300819380,
  "http://example.com/is_root": true
}

第三個部分 Sign 則是透過 JOSE Header 的演算法將 JOSE Header 和 JWT Claims Set 一起做 hash 的結果
(Ref: http://www.sitepoint.com/using-json-web-tokens-node-js/)

JWT 流程

主要流程是

  1. User 送出request到Server要通過 authentication
  2. Server 判斷(檢查密碼等)通過 auth 後,把 user 的資訊包到 token 內
  3. Server 用一個 secret 去 hash 這份 token,並把hash過後的sign放到 token 的最後一個欄位
  4. Server 把 JWT 回傳給 user
  5. User 之後送 request 給 server 的時候都在 header 中夾帶這個 token
  6. Server 收到有 token 的 request 後,用同一份 secret 去解開這個 token,拿到 userid 並辨識出 user

參考