現在最も普及しているクロスドメイン認証ソリューションである JSON Web Token (略称 JWT) の原理と使い方を紹介します。
1. クロスドメイン認証の問題
インターネットサービスはユーザー認証と切っても切れない関係にあります。一般的なプロセスは次のとおりです。
1. ユーザーはユーザー名とパスワードをサーバーに送信します。
2. サーバーが認証されたら、ユーザーの役割、ログイン時間などの関連データを現在のセッション (セッション) に保存します。
3. サーバーは session_id をユーザーに返し、それをユーザーの Cookie に書き込みます。
4. ユーザーの後続のリクエストごとに、session_id が cookie を通じてサーバーに送信されます。
5. サーバーは session_id を受信し、前の期間に保存されたデータを見つけて、ユーザーの ID を認識します。
このモデルの問題点は、スケーラビリティ(拡張性)が良くないことです。もちろん、1台のマシンであれば問題ありませんが、サーバークラスタやクロスドメインのサービス指向アーキテクチャの場合は、セッションデータの共有が必要となり、各サーバーがセッションを読み取ることができます。
たとえば、Web サイト A と Web サイト B は、同じ会社の提携サービスです。ユーザーがいずれかの Web サイトにログインしている限り、別の Web サイトにアクセスしたときに自動的にログインすることが求められていますが、これはどのように実現できるのでしょうか?
1 つの解決策は、データベースまたは他の永続層に書き込まれるセッション データの永続化です。リクエストを受信した後、さまざまなサービスが永続層にデータをリクエストします。このスキームの利点は構造が明確であることですが、欠点はエンジニアリングの量が比較的大きいことです。さらに、永続層がハングアップすると、単一障害点になります。
もう 1 つの解決策は、サーバーがセッション データをまったく保存せず、すべてのデータがクライアントに保存され、各リクエストがサーバーに送り返されるというものです。JWTはこの種のスキームの代表的なものです。
2. JWTの原理
JWT の原理は、次に示すように、サーバーが認証された後、JSON オブジェクトが生成されてユーザーに送り返されることです。
{ "姓名": "张三", "角色": "管理员", "到期时间": "2018年7月1日0点0分" }
将来、ユーザーがサーバーと通信するときは、この JSON オブジェクトを送り返す必要があります。サーバーはこのオブジェクトに完全に依存してユーザーを識別します。ユーザーによるデータの改ざんを防ぐために、サーバーはこのオブジェクトの生成時に署名を追加します (詳細は以下を参照)。
サーバーはセッション データを保存しません。つまり、サーバーはステートレスになるため、拡張が容易になります。
3. JWTのデータ構造
実際のJWTは以下のようになります。
.
これは、ドット ( ) で 3 つの部分に区切られた長い文字列です。なお、JWT内では改行はありませんが、ここでは表示のために複数行に分けて記述しています。
JWT の 3 つの部分は、順番に次のとおりです。
- ヘッダ
- ペイロード
- サイン
一行で書くと以下のようになります。
Header.Payload.Signature
これら 3 つの部分については、以下で順番に説明します。
3.1 ヘッダー
ヘッダー部分は、JWT のメタデータを記述する JSON オブジェクトで、通常は次のようになります。
{ "alg": "HS256", "typ": "JWT" }
上記のコードでは、alg
属性は署名アルゴリズム (アルゴリズム) を示し、デフォルトは HMAC SHA256 (HS256 と記述されます)、属性はtyp
トークンの種類 (トークン) を示し、JWT トークンは一律に と記述されますJWT
。
最後に、Base64URL アルゴリズム (以下を参照) を使用して、上記の JSON オブジェクトを文字列に変換します。
3.2 ペイロード
ペイロード部分も JSON オブジェクトであり、渡す必要がある実際のデータを保存するために使用されます。JWT では、選択用に 7 つの公式フィールドが指定されています。
- iss (発行者): 発行者
- exp (有効期限): 有効期限
- サブ (件名): 件名
- aud (聴衆): 聴衆
- nbf (Not Before): 有効時間
- iat (発行時刻): 発行時刻
- jti (JWT ID): 番号
公式フィールドに加えて、このセクションではプライベート フィールドも定義できます。次に例を示します。
{ "sub": "1234567890", "name": "John Doe", "admin": true }
JWT はデフォルトでは暗号化されておらず、誰でも読み取ることができるため、このセクションには秘密情報を入力しないでください。
この JSON オブジェクトも、Base64URL アルゴリズムを使用して文字列に変換する必要があります。
3.3 署名
署名部分は、データの改ざんを防ぐための最初の 2 つの部分の署名です。
まず、キー (シークレット) を指定する必要があります。このキーはサーバーのみが知っており、ユーザーに開示することはできません。次に、ヘッダーで指定された署名アルゴリズム (デフォルトは HMAC SHA256) を使用し、次の式に従って署名を生成します。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
署名を計算したら、Header、Payload、Signature の 3 つの部分を文字列に結合し、各部分をドット ( .
) で区切ってユーザーに返します。
3.4 Base64URL
前述したように、ヘッダーとペイロードのシリアル化のアルゴリズムは Base64URL です。このアルゴリズムは基本的に Base64 アルゴリズムと似ていますが、いくつかの小さな違いがあります。
JWT はトークン (トークン) として使用され、場合によっては URL に配置されることがあります (api.example.com/?token=xxx など)。Base64 には 3 つの文字と があり+
、これらは URL 内で特別な意味を持つため、省略する、に置き換える、に置き換えるなど、置き換える必要があります 。これは Base64URL アルゴリズムです。/
=
=
+
-
/
_
4.JWTの使い方
クライアントはサーバーから返された JWT を受け取ります。JWT は Cookie または localStorage に保存できます。
その後、クライアントはサーバーと通信するたびに、この JWT を取得する必要があります。Authorization
これを Cookie に入れて自動的に送信することもできますが、ドメインを越えることはできないため、 HTTP リクエストのヘッダー情報フィールドに入れることをお勧めします。
Authorization: Bearer <token>
もう 1 つのアプローチは、ドメインをまたぐ場合、JWT を POST リクエストのデータ本体に配置することです。
JWT の 5 つの特徴
(1) JWT はデフォルトでは暗号化されませんが、暗号化することもできます。元のトークンが生成された後、キーを使用して再度暗号化できます。
(2) JWT が暗号化されていない場合、JWT に機密データを書き込むことができません。
(3) JWT は認証だけでなく情報交換にも利用できます。JWT を効果的に使用すると、サーバーがデータベースにクエリを実行する回数を減らすことができます。
(4) JWT の最大の欠点は、サーバーがセッション状態を保存しないため、使用中にトークンを取り消したり、トークンの権限を変更したりすることができないことです。つまり、JWT が発行されると、サーバーが追加のロジックをデプロイしない限り、有効期限が切れるまで有効なままになります。
(5) JWT自体に認証情報が含まれており、一度漏洩すると誰でもトークンの全ての権限を取得できてしまいます。不正流用を減らすためには、JWT の有効期間を比較的短く設定する必要があります。いくつかのより重要な権限については、ユーザーは使用時に再度認証される必要があります。
(6) 不正流用を防止するため、JWT は HTTP プロトコルを使用した平文で送信するのではなく、HTTPS プロトコルを使用して送信する必要があります。