Google Code Prettify

2017年8月3日 星期四

JWT 認證在 RESTful service 上的應用 (1) - JWT 說明

RESTful 服務現在大量取代了原本 SOAP 擔任的系統間溝通的任務,尤其是企業內部的系統間溝通,新開發的程式很少會選用 SOAP。RESTful 程式的輕薄短小是它的優點,也是廣受歡迎的最重要原因,所以系統間認證時,就流行以 JWT (JSON Web Token) 方式進行認證,JWT 只是在 http 的 header 加一段很短的訊息,不會讓兩個系統間交換的電文大量膨脹。這篇文章會有點長,所以分成三篇,如下:
  1. JWT 認證在 RESTful service 上的應用 (1) - JWT 說明
  2. JWT 認證在 RESTful service 上的應用 (2) - RESTful service
  3. JWT 認證在 RESTful service 上的應用 (3) - Angular
底下開始是第一篇 …  JWT 說明

JWT 是在 http 的 header 中放入一小段加密的字串,通常會放在命名為 Authorization 的欄位裡,這段字串分三段,每段以句點 ( . ) 隔開,這三段分別為 Header、Payload、Signature,Header 和 Payload 是 JSON 格式並且以 Base64 編碼的字串,Signature 是簽章,用來驗證前面的 header、payload 有沒有被竄改,說明如下:
  • Header
通常 header 裡會有兩個欄位,如下,alg 寫著加密的演算法,typ 固定就寫 JWT,如下 HS256 表示 SHA-256,加密演算法不限定用對稱或不對稱的方式。
{
    "alg": "HS256",
    "typ": "JWT"
}
  • Payload
這一段有一些常用的欄位 -- iss (issuer, JWT 的簽發者)、iat (issuedAt, 發出訊息的時間)、exp (expiration time, 逾時時間)、sub (subject, client端發出訊息者)、aud (audience, 接收訊息者) 等等。這些欄位算是 metadata 不一定要有值,當然啦~ 如果 service 端有要求,就要放上相關的值,iat、exp 都是整數,以1970/01/01 00:00:00 開始計算的秒數。除了那些欄位外,也可以自訂一些其它欄位,舉例如下:
{
     "iss": "steven",
     "company": "Google",
     "sub": "test"
}
company 是自訂欄位 … Header、Payload 當然會以 Base64 編碼。
  • Signature
這個簽章固定用下面的方式產生:
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, key);
也就是把 header、payload 分別以 Base64 編碼相加,中間以 . 隔開,然後再用 key (Base64編碼) 簽章,這個 key 實際的值是二進位碼,以 Base64 記錄可能像是 lTjj86wJMqJ5RmnxVO4GppLG7b/U599mPCZM40CKWXaa3MXv5TQIfjXsiPcpI/s8Ou4Q2+uDhmO3+DAcdcOhZg==,如果是以 Base64 記錄,上面第 2 行程式的 key 就要再用函數將它轉換為二進位,簽章的演算法記錄在 Header 的 alg 欄位,如果以上面所記 HS256 來看,就是 SHA-256,簽章後的結果再以 Base64 編碼,

上面的 Signature 產生方式如果以 Java 來寫,如下:
String header64 = Base64.encode(header.getBytes());
String payload64 = Base64.encode(payload.getBytes());
        
Mac sha512_HMAC = Mac.getInstance("HmacSHA512");
SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(Base64.decode(key), "HmacSHA512");
sha512_HMAC.init(secret_key);
byte[] mac_data = sha512_HMAC.doFinal((header64 + "." + payload64).getBytes());
String signature = Base64.encode(mac_data);
* 上面的 Java 程式是假定 header 的 alg 值為 HS512 (SHA-512)

產生 signature 後,要將整個 JWT token 放入 Header 的 Authorization,一般會在 token 前加上 "Bearer ",所以會如下: (Bearer 後面有一個空白)
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + header64 + "." + payload64 + "." + signature);

沒有留言:

張貼留言