1. 概要

JSON Web Token (JWT)は、RESTAPIセキュリティでよく使用されます。 トークンはSpringSecurity OAuth などのフレームワークで解析できますが、独自のコードでトークンを処理したい場合があります。

このチュートリアルでは、JWTの整合性をデコードして検証します。

2. JWTの構造

まず、JWTの構造を理解しましょう。

  • ヘッダ
  • ペイロード(多くの場合、本体と呼ばれます)
  • サイン

署名はオプションです。 有効なJWTは、ヘッダーセクションとペイロードセクションのみで構成できます。 ただし、署名セクションを使用して、セキュリティ認証のヘッダーとペイロードの内容を確認します。

セクションは、ピリオド(’。’)区切り文字で区切られたbase64urlでエンコードされた文字列として表されます。 設計上、誰でもJWTをデコードして、ヘッダーセクションとペイロードセクションの内容を読み取ることができます。 ただし、トークンの整合性を検証するために、署名の作成に使用される秘密鍵にアクセスする必要があります。

最も一般的には、JWTにはユーザーの「クレーム」が含まれています。 これらはユーザーに関するデータを表し、APIを使用して権限を付与したり、トークンを提供するユーザーを追跡したりできます。 トークンをデコードすると、アプリケーションはデータを使用でき、検証により、アプリケーションはJWTが信頼できるソースによって生成されたことを信頼できます。

Javaでトークンをデコードして検証する方法を見てみましょう。

3. JWTのデコード

組み込みのJava関数を使用してトークンをデコードできます。

まず、トークンをセクションに分割しましょう。

String[] chunks = token.split("\\.");

String.split に渡される正規表現では、エスケープされた‘。’文字を使用して’。’を回避していることに注意してください。 「任意の文字」を意味します。

チャンク配列には、JWTのセクションに対応する2つまたは3つの要素が含まれているはずです。

次に、base64urlデコーダーを使用してヘッダーとペイロードの部分をデコードしましょう。

Base64.Decoder decoder = Base64.getUrlDecoder();

String header = new String(decoder.decode(chunks[0]));
String payload = new String(decoder.decode(chunks[1]));

このコードをJWTで実行してみましょう(オンラインでデコードして結果を比較できます):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJhZWxkdW5nIFVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.qH7Zj_m3kY69kxhaQXTa-ivIpytKXXjZc1ZSmapZnGE

出力は、デコードされたヘッダーに任意のペイロードを提供します。

{"alg":"HS256","typ":"JWT"}{"sub":"1234567890","name":"Baeldung User","iat":1516239022}

JWTでヘッダーセクションとペイロードセクションのみが定義されている場合は、終了して情報が正常にデコードされます。

4. JWTの検証

次に、署名セクションを使用して、ヘッダーとペイロードの整合性を検証し、それらが変更されていないことを確認できます。

4.1. 依存関係

検証のために、jjwtpom.xmlに追加できます。

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>

このライブラリのバージョン0.7.0以降が必要であることに注意してください。

4.2. 署名アルゴリズムとキー仕様の構成

ペイロードとヘッダーの検証を開始するには、トークンの署名に最初に使用された署名アルゴリズムと秘密鍵の両方が必要です。

SignatureAlgorithm sa = HS256;
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), sa.getJcaName());

この例では、署名アルゴリズムをHS256にハードコーディングしています。 ただし、ヘッダーのJSONをデコードし、algフィールドを読み取ってこの値を取得することはできます。

また、変数secretKeyは秘密鍵の文字列表現であることに注意してください。 これは、構成を介して、またはJWTを発行するサービスによって公開されるRESTAPIを介してアプリケーションに提供する場合があります。

4.3. 検証の実行

署名アルゴリズムと秘密鍵ができたので、検証の実行を開始できます。

ヘッダーとペイロードを署名されていないJWTに再結合し、それらを「。」で結合してみましょう。 デリミタ:

String tokenWithoutSignature = chunks[0] + "." + chunks[1];
String signature = chunks[2];

これで、署名されていないトークンと提供された署名ができました。 ライブラリを使用して検証できます。

DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, secretKeySpec);

if (!validator.isValid(tokenWithoutSignature, signature)) {
    throw new Exception("Could not verify JWT token integrity!");
}

これを分解しましょう。

まず、選択したアルゴリズムとシークレットを使用してバリデーターを作成します。 次に、署名されていないトークンデータと提供された署名を提供します。

次に、バリデーターは新しい署名を生成し、提供された署名と比較します。 それらが等しい場合、ヘッダーとペイロードの整合性を検証しました。

5. 結論

この記事では、JWTの構造とそれをJSONにデコードする方法について説明しました。

次に、ライブラリを使用して、署名、アルゴリズム、秘密鍵を使用してトークンの整合性を検証しました。

いつものように、この記事のコード例はGitHubにあります。