jwt-logout プラグインは、Redis を使用して JSON Web Token (JWT) の弱い状態管理を実装し、JWT の事前無効化と単一デバイスログイン制御を可能にします。たとえば、ユーザーが新しいデバイスからログインすると、以前のセッションは自動的に無効化されます。
プラグインのタイプ
認証および権限付与のためのプラグインです。
フィールド
|
フィールド |
データ型 |
必須 |
デフォルト値 |
説明 |
|
jwks |
string |
いいえ |
- |
JWT 検証用の JSON 文字列。このプラグインを jwt-auth プラグインと共に使用する場合は不要です。詳細については、「JSON Web Key (JWK)」をご参照ください。 |
|
clock_skew |
数値 |
いいえ |
60 |
JWT の |
|
token_header |
string |
いいえ |
Authorization |
JWT を抽出するリクエストヘッダー。 |
|
token_prefix |
string |
いいえ |
"Bearer" |
ヘッダー値のプレフィックス。プレフィックスを削除した後、残りの部分が JWT として使用されます。 |
|
redis |
Redis |
はい |
- |
Redis サービスの設定。 |
|
logout |
Logout |
いいえ |
- |
JWT ログアウト機能の設定。設定しない場合、JWT ログアウトは無効になります。 |
|
login |
Login |
いいえ |
- |
JWT 単一デバイスログイン機能の設定。設定しない場合、単一デバイスログインは無効になります。 |
次の表は、Redis タイプの構成フィールドについて説明します。
|
フィールド |
データ型 |
必須 |
デフォルト値 |
説明 |
|
service |
string |
はい |
- |
Redis サービスの名前。例:
|
|
port |
数値 |
はい |
- |
Redis サービスのポート番号。 |
|
username |
string |
いいえ |
- |
Redis の AUTH コマンドのユーザー名。 |
|
password |
string |
いいえ |
- |
Redis の AUTH コマンドのパスワード。 |
|
timeout |
数値 |
いいえ |
1000 |
Redis コマンドのタイムアウト期間。単位: ミリ秒。 |
次の表は、Logout タイプの構成フィールドについて説明します。
|
フィールド |
データ型 |
必須 |
デフォルト値 |
説明 |
|
key_prefix |
string |
いいえ |
higress_jwt_logout_ |
Redis に保存されているキーのプレフィックス。 |
|
key |
文字列の配列 |
いいえ |
["jti"] |
JWT ペイロード内で、JWT を識別するためのキー。複数の JWT ペイロードに同じキーが出現する場合、それらの JWT は同一として扱われます。JWT ペイロードにキーが存在しない場合、401 invalid token エラーが返されます。 |
|
path |
string |
いいえ |
/jwt_logout |
URL パスのサフィックスと一致させるために使用される文字列。一致した場合、現在のリクエストの JWT は無効化され、使用できなくなります。 |
|
error_status |
数値 |
いいえ |
401 |
ログアウトした JWT が使用されたときに返される HTTP ステータスコード。 |
|
error_body |
string |
いいえ |
'{"message":"invalid token"}' |
ログアウトした JWT が使用されたときに返されるレスポンスボディ。 |
|
ttl |
数値 |
いいえ |
- |
Redis に保存されているキーの有効期間 (TTL)。単位: 秒。ログアウト後に JWT が無効化された状態を維持する期間を決定します。指定しない場合、TTL はペイロードの |
次の表は、Login タイプの構成フィールドについて説明します。
|
フィールド |
データ型 |
必須 |
デフォルト値 |
説明 |
|
key_prefix |
string |
いいえ |
higress_jwt_login_ |
Redis に保存されているキーのプレフィックス。 |
|
key |
文字列の配列 |
いいえ |
["iss","aud","sub"] |
JWT ペイロード内の単一デバイスログイン識別子。現在のリクエストの JWT に、以前に認証された JWT と同じフィールド値が含まれているが、完全に同じ JWT ではない場合、リクエストは重複ログインとして拒否されます。認証された JWT の Redis キーが期限切れになるまでアクセスは拒否されます。JWT ペイロードにフィールドが存在しない場合、401 invalid token エラーが返されます。 |
|
path |
string |
いいえ |
/jwt_login |
URL パスのサフィックスと一致させるために使用される文字列。一致した場合、現在のリクエストの JWT がアクティブなセッショントークンになり、同じペイロード特性を持つ以前に認証された JWT は無効化されます。 |
|
error_status |
数値 |
いいえ |
403 |
重複ログイン試行に対して返される HTTP ステータスコード。 |
|
error_body |
string |
いいえ |
'{"message":"already login on other device"}' |
重複ログイン試行に対して返されるレスポンスボディ。 |
|
ttl |
数値 |
いいえ |
- |
Redis に保存されているキーの TTL。単位: 秒。単一デバイスログイン JWT の有効期間を決定します。指定しない場合、TTL はペイロードの |
設定例
ApsaraDB for Redis インスタンスの使用
-
ApsaraDB for Redis インスタンスを作成します。詳細については、「概要」をご参照ください。
-
ログアウトおよびログイン設定を有効にします。プラグインがリクエストを処理すると、ログアウト設定チェック用に Redis 読み取りリクエストが 1 回、ログイン設定チェック用に Redis 読み取りリクエストが 2 回生成されます。ログアウトや初回ログインなどのまれなケースでは、追加で Redis 書き込みリクエストが 2 回生成されます。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: 6379Redis サービスにパスワードを設定する場合は、次の内容を追加します:
redis: service: redis.dns port: 6379 password: ****** # 指定したパスワードを入力します。
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#testは 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#testは 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 ステータスコード |
エラーメッセージ |
理由 |
|
401 |
invalid token |
リクエストヘッダーに JWT が提供されていない、JWT 形式が無効、または JWT が期限切れか無効化されています。 |
|
500 |
redis server error |
ApsaraDB for Redis インスタンスに到達できないか、接続がタイムアウトしました。 |