jwt-logout プラグインは、Redis を使用して JSON Web Token(JWT)の弱い状態管理を実装します。このプラグインは、JWT がプロアクティブなログアウトをサポートしていないという問題を解決します。また、このプラグインを使用して、アカウントのシングルデバイスログインを実装することもできます。たとえば、アカウントを使用して別のデバイスにログインすると、アカウントはデバイスから自動的にログアウトされます。
このプラグインは、ゲートウェイのバージョンが 1.2.32 以降の場合に使用できます。
プラグインの種類
認証プラグイン。
フィールド
名前 | データ型 | 必須 | デフォルト値 | 説明 |
jwks | string | いいえ | - | JWT の検証に使用する JSON 文字列。このプラグインを jwt-auth プラグインと一緒に使用する場合、このフィールドを設定する必要はありません。詳細については、「JSON Web Key (JWK)」をご参照ください。 |
clock_skew | number | いいえ | 60 | JWT の exp フィールドと iat フィールドをチェックするときに許容されるクロックオフセット。単位:秒。 |
token_header | string | いいえ | Authorization | JWT を抽出できるリクエストヘッダー。 |
token_prefix | string | いいえ | "Bearer" | リクエストヘッダーの値のプレフィックス。プレフィックスが削除された後、リクエストヘッダーの残りの部分が JWT コンテンツとして使用されます。 |
redis | Redis | はい | - | Redis サービス構成。 |
logout | Logout | いいえ | - | JWT ログアウト機能を実装するために使用される構成。このフィールドが構成されていない場合、JWT ログアウト機能は無効になります。 |
login | Login | いいえ | - | JWT シングルデバイスログイン機能を実装するために使用される構成。このフィールドが構成されていない場合、JWT シングルデバイスログイン機能は無効になります。 |
次の表は、Redis タイプの構成フィールドについて説明しています。
名前 | データ型 | 必須 | デフォルト値 | 説明 |
service | string | はい | - | Redis サービスの名前。例:
|
port | number | はい | - | Redis サービスのポート番号。 |
username | string | いいえ | - | Redis AUTH コマンドで使用されるユーザー名。 |
password | string | いいえ | - | Redis AUTH コマンドで使用されるパスワード。 |
timeout | number | いいえ | 1000 | Redis コマンドのタイムアウト期間。単位:ミリ秒。 |
次の表は、ログアウトタイプの構成フィールドについて説明しています。
名前 | データ型 | 必須 | デフォルト値 | 説明 |
key_prefix | string | いいえ | higress_jwt_logout_ | Redis に保存されているキーのプレフィックス。 |
key | string の配列 | いいえ | ["jti"] | JWT を識別するために JWT ペイロードにあるキー。複数の JWT のペイロードに同じキーが含まれている場合、それらの JWT は同じものと見なされます。JWT ペイロードにキーが含まれていない場合、エラー 401 無効なトークンが報告されます。 |
path | string | いいえ | /jwt_logout | URL パスサフィックスの照合に使用される文字列。URL パスサフィックスが指定された文字列と一致する場合、現在のリクエストで JWT を使用しているアカウントはログアウトされます。JWT はそれ以上使用できません。 |
error_status | number | いいえ | 401 | ログアウト時に返されるエラーコード。 |
error_body | string | いいえ | '{"message":"invalid token"}' | ログアウト時のレスポンスの本文。 |
ttl | number | いいえ | - | Redis に保存されているキーの Time To Live(TTL)。単位:秒。この構成は、ログアウト後に JWT が無効になる期間を決定します。この構成が指定されていない場合、TTL はペイロードの exp フィールドの値から現在の時刻を引いた値と同じになります。ペイロードに exp フィールドが含まれていない場合、デフォルトの TTL は 86,400 秒(24 時間)です。 |
次の表は、ログインタイプの構成フィールドについて説明しています。
名前 | データ型 | 必須 | デフォルト値 | 説明 |
key_prefix | string | いいえ | higress_jwt_logout_ | Redis に保存されているキーのプレフィックス。 |
key | string の配列 | いいえ | ["iss","aud","sub"] | JWT ペイロードのシングルデバイスログイン識別子。現在のリクエストの JWT ペイロードに同じフィールドが含まれているが、リクエスト JWT がログイン成功時の JWT と完全に同じでない場合、システムは現在のリクエストを繰り返しのログインリクエストと見なし、リクエストを拒否します。Redis 内のログイン成功時の JWT のキーが期限切れになるまで、システムはリクエストを許可します。現在のリクエストの JWT ペイロードにフィールドが含まれていない場合、エラー 401 無効なトークンが報告されます。 |
path | string | いいえ | /jwt_login | URL パスサフィックスの照合に使用される文字列。URL パスサフィックスが指定された文字列と一致する場合、アカウントは現在のリクエストの JWT を使用してシステムに強制的にログインされます。ログイン成功時に使用され、同じペイロード特性を持つ JWT は、ログアウト時に無効になります。 |
error_status | number | いいえ | 403 | 繰り返しのログインに対して返されるエラーコード。 |
error_body | string | いいえ | '{"message":"already login on other device"}' | 繰り返しのログインに対するレスポンス本文。 |
ttl | number | いいえ | - | Redis に保存されているキーの TTL。単位:秒。このフィールドは、シングルデバイスログインに使用される JWT の有効期間を決定します。このフィールドが指定されていない場合、TTL はペイロードの exp フィールドの値から現在の時刻を引いた値と同じになります。ペイロードに exp フィールドが含まれていない場合、デフォルトの TTL は 86,400 秒(24 時間)です。 |
構成例
ApsaraDB for Redis インスタンスを使用する
ApsaraDB for Redis インスタンスを作成します。詳細については、「概要」をご参照ください。
ログアウトとログインの構成を有効にします。プラグインがリクエストを処理すると、ログアウト構成チェックのための Redis 読み取りリクエストが 1 つ、ログイン構成チェックのための Redis 読み取りリクエストが 2 つ生成されます。ログアウトや初回ログインなどのまれなケースでは、さらに 2 つの Redis 書き込みリクエストが生成されます。ApsaraDB for Redis インスタンスの容量を見積もるには、プラグインによって処理されるリクエストのスループットに 2 を掛けます。
ApsaraDB for Redis インスタンスを構成した後、インスタンスの VPC エンドポイントを取得します。例:r-xxxxxxx.redis.rds.aliyuncs.com。
サービスを追加します。[DNS ドメイン名] を [サービスソース] ドロップダウンリストから選択し、[サービスポート] フィールドに Redis ポート番号(ほとんどの場合 6379)を入力し、[ドメイン名] フィールドに VPC エンドポイントを入力し、[TLS モード] ドロップダウンリストから [無効] を選択します。詳細については、「サービスの追加」をご参照ください。
ApsaraDB for Redis インスタンスに接続するために、プラグイン構成に次のコンテンツを追加します。
redis: service: redis.dns port: 6379
Redis サービスのパスワードを設定する場合は、次のコンテンツを追加します。
redis: service: redis.dns port: 6379 password: ****** # 指定したパスワードを入力します。
JWT ログアウト機能を実装する
シナリオ
JWT はステートレストークンです。JWT が発行されると、期限切れになるまで有効です。jwt-logout プラグインを使用して、特定の JWT が使用されたときに強制ログアウトを実装できます。
仕組み
リクエストパスのサフィックスがプラグイン構成のパスと一致する場合、ログアウトメカニズムがトリガーされます。ApsaraDB for Redis インスタンスは、ログアウトの JWT を記録します。ApsaraDB for Redis インスタンスに保存されているキーは、構成されたプレフィックスと、現在の JWT ペイロードから抽出されたキーと値で構成されます。
リクエストで渡された JWT のペイロードが ApsaraDB for Redis インスタンスに保存されているキーの特性と一致する場合、システムは現在の JWT を無効と見なし、リクエストを拒否します。
ApsaraDB for Redis インスタンスに保存されているキーのデフォルトの有効期限は、JWT ペイロードの
exp
フィールドに基づいて計算されます。これは、JWT が期限切れになるまでキーを保存できることを意味します。
プラグインのデフォルトの推奨ログアウトキーは ["jti"] で、
jti
は JWT を一意に識別するペイロードフィールドです。JWT 標準で指定されているように、JWT ペイロードのjti
値が ApsaraDB for Redis インスタンスに記録されている値と同じであれば、JWT 全体が完全に同じです。したがって、jti を使用して JWT のログアウトステータスをマークできます。ApsaraDB for Redis インスタンスに保存されているキーの連結ルール:
<key_prefix><PayloadKey>##<PayloadValue>
。PayloadKey は、番号記号(#)で区切られたキーのリストです。PayloadValue は、番号記号(#)で区切られたキー値のリストです。例:higress_jwt_logout_jti#iss##xxxxx#abcde
。このプラグインは、ログアウト中に現在のリクエストで渡されたトークンのみを無効にすることができます。上記のキー連結ルールに基づいて、ApsaraDB for Redis コンソールでトークンを手動で指定することもできます。ゲートウェイが ApsaraDB for Redis インスタンスにキーが存在することを検出した場合、ゲートウェイは関連する JWT に基づくアクセスリクエストを拒否します。
例
プラグイン構成:
redis:
service: redis.dns
port: 6379
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
logout:
path: "/jwt_logout"
key: ["jti"]
error_status: 401
error_body: |
{"message":"invalid token"}
ログアウトをトリガーします。
curl http://xxx.hello.com/test/jwt_logout -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ4eHh4IiwiaXNzIjoiYWJjZCIsInN1YiI6InRlc3QiLCJhdWQiOiJ3d3cudGVzdC5jb20iLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.tmKF6qc1mOWNyCCzBOT2XKNoEGeEgr3EbhTKAQfq1io' # 次の結果が返されます。 {"message": "logout success"}
トークンペイロード:
{ "jti": "xxxx", "iss": "abcd", "sub": "test", "aud": "www.test.com", "iat": 1665660527, "exp": 1865673819 }
この場合、キー
higress_jwt_logout_jti##xxxx
が ApsaraDB for Redis インスタンスに保存されます。ログアウト後、JWT に基づくアクセスは許可されません。
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ4eHh4IiwiaXNzIjoiYWJjZCIsInN1YiI6InRlc3QiLCJhdWQiOiJ3d3cudGVzdC5jb20iLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.tmKF6qc1mOWNyCCzBOT2XKNoEGeEgr3EbhTKAQfq1io' # 次の結果が返されます。 {"message":"invalid token"}
JWT に基づく強制ログアウトを実装する
シナリオ
アカウントが複数のデバイスへのログインに使用され、ログインに JWT 認証が使用される場合、デバイスごとに異なる JWT が発行される可能性があります。この場合、jwt-logout プラグインを使用して、アカウントが一度に 1 つのデバイスのみにログインできるようにすることができます。
仕組み
リクエストが開始されると、システムは現在のリクエストから JWT を抽出し、構成されたプレフィックスと現在の JWT ペイロードから抽出されたキーと値に基づいて Redis キーを構成します。次に、システムは ApsaraDB for Redis インスタンスでキーに対応する値をクエリします。値が現在の JWT のキー値と一致しない場合、システムはアクセスリクエストを拒否します。
値が存在しない場合、現在の JWT が Redis キーに書き込まれます。ApsaraDB for Redis インスタンスのキーのデフォルトの有効期限は、JWT ペイロードの
exp
フィールドに基づいて計算されます。これは、JWT が期限切れになるまでキーを保存できることを意味します。JWT の有効期間中、同じペイロード特性を持つ他の JWT に基づくアクセスリクエストは許可されません。リクエストサフィックスがプラグイン構成の
path
と一致する場合、強制ログアウトメカニズムがトリガーされ、現在の JWT が関連付けられた Redis キーの値に書き込まれます。ログイン成功時に使用され、同じペイロード特性を持つ JWT は、ログアウト時に無効になります。これにより、シングルデバイスログインが保証されます。
プラグインのデフォルトの推奨ログインキーは ["iss","aud","sub"] です。キーでは、
iss
は JWT 発行者を示し、aud
は JWT の使用シナリオを示し、sub
は JWT のサブジェクトを示します。ほとんどの場合、この組み合わせを使用してシングルデバイスログインを保証できます。ApsaraDB for Redis インスタンスに保存されているキーの連結ルール:
<key_prefix><PayloadKey>##<PayloadValue>
。PayloadKey は、番号記号(#)で区切られたキーのリストです。PayloadValue は、番号記号(#)で区切られたキー値のリストです。例:higress_jwt_login_iss#aud#sub##xxxxx#abcde#fffff
。同じペイロード特性を持つ JWT の場合、リクエストに使用された最初の JWT が ApsaraDB for Redis インスタンスに自動的に書き込まれます。これは、プラグインのシングルデバイスログイン機能を実装するのに役立ちます。したがって、強制ログインの API 操作を呼び出す必要はありません。ログインシナリオで強制ログアウトが必要な場合にのみ、API 操作を呼び出す必要があります。
ログアウトロジックをトリガーすると、ApsaraDB for Redis インスタンスに保存されている現在の JWT のログインキーがクリアされます。このルールは、JWT ログアウト機能を有効にした場合にも適用されます。
例
プラグイン構成:
redis:
service: redis.dns
port: 6379
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
login:
path: "/jwt_login"
key: ["iss","aud","sub"]
error_status: 403
error_body: |
{"message":"already login on other device"}
ログイン操作を正常に実行します。
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ6enp6IiwiaXNzIjoiYWJjZCIsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6InRlc3QiLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.WljMr5ucxfLF8SmeaaL25c0QG3IX04HoD0als9gglYg'
トークンペイロード:
{ "jti": "zzzz", "iss": "abcd", "aud": "www.example.com", "sub": "test", "iat": 1665660527, "exp": 1865673819 }
この場合、キー
higress_jwt_login_iss#aud#sub##abcd#www.example.com#abcd
が ApsaraDB for Redis インスタンスに保存され、値は現在の JWT の値と同じです。リクエストの JWT ペイロードが同じペイロード特性を持つ場合、アクセスリクエストを拒否します。
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ5eXl5eSIsImlzcyI6ImFiY2QiLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0IiwiaWF0IjoxNjY1NjYwNTI5LCJleHAiOjE4NjU2NzM4MTl9.6vi6eKPWSKHQxfzBPrj3-SWI4Q5zGtWhqp38JIN3FEo' # 次の結果が返されます。 {"message":"already login on other device"}
トークンペイロード:
{ "jti": "yyyyy", "iss": "abcd", "aud": "www.example.com", "sub": "test", "iat": 1665660527, "exp": 1865673819 }
現在の JWT と手順 1 の JWT のペイロード特性は同じです。ただし、非特性フィールド
jti
は 2 つの JWT で異なります。したがって、JWT に基づくアクセスリクエストは拒否されます。強制ログインを実行します。
curl http://xxx.hello.com/test/jwt_login -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ5eXl5eSIsImlzcyI6ImFiY2QiLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0IiwiaWF0IjoxNjY1NjYwNTI5LCJleHAiOjE4NjU2NzM4MTl9.6vi6eKPWSKHQxfzBPrj3-SWI4Q5zGtWhqp38JIN3FEo' # 次の結果が返されます。 {"message":"login success"}
この場合、キー
higress_jwt_login_iss#aud#sub##abcd#www.example.com#abcd
が ApsaraDB for Redis インスタンスに保存され、値は現在の JWT の値に置き換えられます。現在の JWT を使用してページに正常にアクセスします。
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ5eXl5eSIsImlzcyI6ImFiY2QiLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0IiwiaWF0IjoxNjY1NjYwNTI5LCJleHAiOjE4NjU2NzM4MTl9.6vi6eKPWSKHQxfzBPrj3-SWI4Q5zGtWhqp38JIN3FEo'
手順 1 の JWT を使用すると、エラーメッセージが返されます。
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ4eHh4IiwiaXNzIjoiYWJjZCIsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6InRlc3QiLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.P0WtBTHJzUJvklu9q8XSRszfPbgojrZHg7t4ZaYfKGo' # 次の結果が返されます。 {"message":"already login on other device"}
HTTP ステータスコード
HTTP ステータスコード | エラーメッセージ | 理由 |
401 | invalid token | リクエストヘッダーに JWT が提供されていないか、JWT の形式が無効であるか、JWT が期限切れです。 |
500 | redis server error | ApsaraDB for Redis インスタンスへのアクセスがタイムアウトまたは失敗しました。 |