JWT ペイロードを Java POJO に変換する

標準 JWT クレーム(iss、sub、aud、exp、iat、nbf、jti)とアプリ固有クレームを含む Java POJO を生成します。

Real-world

詳細な説明

Java での JWT ペイロード

JSON Web Token のペイロードは RFC 7519 で定義された標準クレームと、アプリが追加した任意クレームを含む JSON オブジェクトです。

標準的な JWT ペイロード

{
  "iss": "https://auth.example.com",
  "sub": "user_42",
  "aud": "api.example.com",
  "exp": 1700000000,
  "iat": 1699996400,
  "nbf": 1699996400,
  "jti": "a1b2c3d4",
  "scope": "read:profile write:profile",
  "roles": ["admin", "billing"]
}

生成される POJO

package com.example.security;

import java.util.List;

public class JwtPayload {
    private String iss;       // 発行者
    private String sub;       // サブジェクト
    private String aud;       // オーディエンス
    private Long exp;         // 失効(Unix タイムスタンプ)
    private Long iat;         // 発行時刻
    private Long nbf;         // 有効開始時刻
    private String jti;       // JWT ID
    private String scope;
    private List<String> roles;
    // アクセサ
}

改善ポイント

自動生成 POJO は十分な出発点ですが、本番に持ち込むには 3 つの改善が有効です。

1. タイムスタンプを Instant に変換

public Instant getExpiry() {
    return Instant.ofEpochSecond(exp);
}

public boolean isExpired() {
    return Instant.now().isAfter(getExpiry());
}

2. aud を String OR List として扱う

JWT 仕様では aud は単一文字列でも配列でも構いません。カスタム Jackson デシリアライザを書くか、@JsonDeserialize(using = AudienceDeserializer.class) で両方をモデル化します。

3. カスタムクレームは Map に集約

標準クレーム以外にアプリ独自フィールドが付くなら次のように受け取れます。

@JsonAnyGetter @JsonAnySetter
private Map<String, Object> customClaims = new HashMap<>();

@JsonAnyGetter@JsonAnySetter により、未知キーが自動的に Map にルーティングされます。

検証済みトークンの扱い

java-jwtnimbus-jose-jwtjjwt などの JWT ライブラリで署名を先に検証し、その後でクレームを POJO にマッピングします。

DecodedJWT jwt = JWT.require(Algorithm.RSA256(publicKey, null))
    .build()
    .verify(token);

ObjectMapper mapper = new ObjectMapper();
JwtPayload payload = mapper.readValue(jwt.getPayload(), JwtPayload.class);

検査用デコード

認証ではなくクレームを覗き見たいだけなら、JWT デコーダー でブラウザ内検証してから POJO 生成に進むと便利です。

ユースケース

認証ミドルウェア、カスタムクレームの抽出、監査ログのいずれにおいても、型付きの JWT ペイロードクラスは有効です。Spring Security、Quarkus Security、Micronaut Security のいずれもカスタム POJO へのデシリアライズに対応できます。

試してみる — JSON to Java

フルツールを開く