JWT

公開日: 2026-08-03 09:56 更新日: 2026-08-03 09:56 2265文字 12 min read ... ページビュー

JWT(JSON Web Token)は、署名と暗号化をサポートし、認証、権限制御、およびシステム間通信に一般的に使用される、システム間のアイデンティティと認可情報を転送するためのコンパクトでURLセーフなトークンフォーマットです。その構造は、ヘッダー、ペイロード、署名の3つの部分で構成され、Base 64 URLエンコーディング伝送を介して、署名はデータの完全性と信頼性を確保しますが、ペイロード内容は暗号化されておらず、情報漏洩のリスクがあります。JWTには、クロス言語、分散検証などの利点がありますが、トークンの取り消しが困難、権限の満了などの制限があり、署名、有効期限、アルゴリズムを厳格に検証し、HTTPS、セキュアストレージなどの対策を組み合わせてセキュリティリスクを防止する必要があります。

JWT

JWT 概要

JWT(JSON Web Token)は、異なるシステム間でアイデンティティと認可関連の情報を渡すためによく使用される、コンパクトでURLセーフな宣言転送フォーマットです。

JWT 自体はトークン形式であり、ログインプロトコルやシングルサインオン方式とは同等ではありません。認証、認可、シングルサインオンプロセスにおけるトークンキャリアとして機能します。

JWTが機密かどうかはカプセル化方法に依存する。一般的な3 段階のJWTはJWSに属し、署名またはメッセージ認証コードのみで保護され、負荷内容は読み取ることができます。負荷はJWEを使用する場合にのみ暗号化されます。

JWTの一般的なアプリケーション

  1. 認証

    ユーザがログインに成功すると、サーバはJWTを発行します。クライアントは後続のリクエストでトークンを運び、サーバは署名、有効期限、発行者、オーディエンスなどの情報を確認してトークンの信頼性を判断します。

  2. アクセス許可

    JWTは、ユーザID、ロール、パーミッション、スコープなどの宣言を運ぶことができます。サーバがトークンチェックを完了すると、これらの宣言に基づいて現在のユーザがターゲットリソースにアクセスできるかどうかを判断します。

  3. システム間情報交換

    送信者はJWTに署名し、受信者はデータが改ざんされているかどうか、トークンが信頼できる発行者によって生成されたかどうかを検証できる。コンテンツの機密性も要求される場合は、JWEまたは他の暗号化チャネルを使用してください。

  4. シングルサインオン

    統一アイデンティティ認証体系では、認証センターはJWTを発行し、各業務システムは約定に従ってトークンを検証することができる。JWTは、トークン発行、リフレッシュ、取り消し、信頼関係も処理する必要があるシングルサインオンスキームのコンポーネントに過ぎません。

JWTの特長

利点は

  • Compact:HTTPリクエストヘッダーでの送信に適しています。
  • クロス言語:JWTは標準化されたフォーマットであり、すべての主要言語に対応する実装があります。
  • **分散認証が容易:リソースサーバは署名をローカルで検証できるため、集中型セッションストレージへの依存を減らすことができます。
  • サポート宣言拡張:標準宣言に加えて、ビジネスカスタム宣言を追加できます。

制限は

  • 発行されたトークンはすぐに取り消すことは容易ではない:通常、有効期間の短縮、ブラックリストの維持、トークンのバージョン番号の使用などのメカニズムが必要です。
  • ペイロードは情報を漏洩する可能性があります:JWSのペイロードはBase 64 URLエンコーディングのみで、暗号化されません。
  • トークンサイズが大きい場合があります:宣言が多すぎると、リクエストごとにネットワークオーバーヘッドが増加します。
  • パーミッションの有効期限が切れる可能性があります:パーミッションをロングトークンに直接書き込むと、データベースパーミッションの変更はすぐに古いトークンに反映されません。

JWTの構成

一般的な署名 JWTは3つの部分で構成され、英語のピリオドで接続されます。

Header.Payload.Signature
image-001
image-001

ヘッダーはJSONオブジェクトで、通常は以下のフィールドを含みます:

  • typ:トークンのタイプ、通常はJWT
  • alg:署名またはメッセージ認証コードアルゴリズム(例:HS256RS256)。

例として:

{
  "typ": "JWT",
  "alg": "HS256"
}

ヘッダはBase 64 URLでエンコードされ、トークンの最初の部分を形成します。

Payload

Payloadは、クレームClaimを格納するために使用される。JWT 仕様は7つの登録宣言名を定義しており、これらのフィールドはすべてオプションです。

宣言英文名称作用
issIssuerさんトークン発行者の識別
subSubject(件名)トークントピックを識別し、通常はユーザーまたはサブジェクトを表す。
audAudience(オーディエンス)トークンの予想される受信者の識別
expExpiration Timeシングルトークンの有効期限の識別
nbfNot Beforeシングル識別トークンがいつまで利用できないか
iat問題が発生したトークン発行時刻の識別
jtiJWT IDトークンを識別する一意の番号

登録宣言に加えて、ビジネス·カスタム宣言を追加できます。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

暗号化されていないJWTペイロードにパスワード、銀行カード番号、ID 番号、鍵などの機密情報を保存しないでください。トークンを取得すると誰でもヘッダーとペイロードをデコードして読み取ることができます。

Signature

署名は、ヘッダーとペイロードが改ざんされていないことを確認し、トークンが対応する鍵を持つ当事者によって発行されたことを確認します。

HS256を例にとると、署名計算プロセスは次のように表現できる。

HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload),
    secret
)

HMACを使用する場合、発行者は認証者と同じ鍵を共有します。RSAまたはECDSAを使用する場合、発行者は秘密鍵で署名し、認証者は公開鍵で署名します。

署名は完全性とソースの信頼性のみを保証し、ペイロードコンテンツを隠すことはできません。

Base64URL

Base 64 URLは、URLとHTTPヘッダの転送に適したBase 64バリアントです。

通常のBase 64と比較して、主に以下の処理を行います。

  • +-に置き換えます。
  • /_に置き換えた。
  • 末尾の=パディング文字を省略します。

Base 64 URLは暗号化アルゴリズムではなくエンコード方式です。

JJWTカプセル化ツールクラスを使用する

次の例は、JJWT 0.12.xに基づいています。構成エントリ内のキーはBase 64エンコードを使用し、デコード後に選択したHMACアルゴリズムの少なくともキー長要件を満たす必要があります。

設定例

jwt:
  secret: ${JWT_SECRET}
  expire-ms: 86400000

JWT_SECRETはコードリポジトリに直接コミットしないでください。環境変数または鍵管理サービスを通じて注入できます。

JWTツール·クラス

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.List;

@Component
public class JwtUtil {

    private final SecretKey key;
    private final long expireMs;

    public JwtUtil(
            @Value("${jwt.secret}") String secret,
            @Value("${jwt.expire-ms}") long expireMs) {
        byte[] keyBytes = Decoders.BASE64.decode(secret);
        this.key = Keys.hmacShaKeyFor(keyBytes);
        this.expireMs = expireMs;
    }

    public String createToken(
            Long userId,
            String username,
            Long roleId,
            List<String> permissions) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + expireMs);

        return Jwts.builder()
                .subject(username)
                .claim("userId", userId)
                .claim("roleId", roleId)
                .claim("permissions", permissions)
                .issuedAt(now)
                .expiration(expiration)
                .signWith(key)
                .compact();
    }

    public Claims parseToken(String token) {
        return Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }

    public boolean isExpired(Claims claims) {
        Date expiration = claims.getExpiration();
        return expiration != null && expiration.before(new Date());
    }
}

signWith(key)は、鍵タイプと長さに基づいて互換性のある署名アルゴリズムを選択します。システムが固定アルゴリズムを必要とする場合は、発行と検証の両方で統一的に構成し、期待されるアルゴリズムのみを受け入れるように制限します。

Claimオブジェクト

ClaimsはJWTにおける宣言の集合を表す. Map<String, Object>のキーバリューアクセス機能を継承しながら、標準宣言を読み取る専用の方法を提供します。

一般的な方法は以下の通り

String subject = claims.getSubject();
Date issuedAt = claims.getIssuedAt();
Date expiration = claims.getExpiration();
String issuer = claims.getIssuer();

カスタム宣言を読み込むときは、ターゲットのタイプを指定できます。

Long userId = claims.get("userId", Long.class);
Long roleId = claims.get("roleId", Long.class);

ジェネリックコレクションの場合、Javaの型消去のため、直接読み取りでは通常生のListしか得られません。ビジネスレベルで個別に変換することも、カスタムデシリアライズスキームを使用することもできます。

@SuppressWarnings("unchecked")
List<String> permissions = claims.get("permissions", List.class);

テストトークンの作成と解決

次のテストでは、同じテストメソッドでトークンを作成して解析し、期限切れや改行で壊れたハードコードされたトークンを使用しません。

import io.jsonwebtoken.Claims;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

@SpringBootTest
class JwtUtilTests {

    @Autowired
    private JwtUtil jwtUtil;

    @Test
    void shouldCreateAndParseToken() {
        List<String> permissions = List.of(
                "permission:query",
                "permission:insert",
                "permission:update",
                "permission:delete",
                "permission:check"
        );

        String token = jwtUtil.createToken(130L, "tom", 3L, permissions);
        Claims claims = jwtUtil.parseToken(token);

        assertEquals("tom", claims.getSubject());
        assertEquals(130L, claims.get("userId", Long.class));
        assertEquals(3L, claims.get("roleId", Long.class));
        assertFalse(jwtUtil.isExpired(claims));

        @SuppressWarnings("unchecked")
        List<String> parsedPermissions = claims.get("permissions", List.class);
        assertEquals(permissions, parsedPermissions);
    }
}

使用上の注意事項

  1. サーバは署名を検証する必要があり、ペイロードだけをデコードする必要があります。
  2. expを検証し、issaudnbfなどの宣言を業務上必要に応じて検証する必要があります。
  3. クライアント自身が指定した任意のアルゴリズムを受け入れないでください。許可されるアルゴリズムとトークンの種類を制限してください。
  4. HMACキーは、キーの代わりに単純なパスワードを使用できないように、十分に長くランダムである必要があります。
  5. アクセストークンは短い有効期間を設定し、長期ログインが必要な場合はリフレッシュトークンメカニズムと連携してください。
  6. HTTPSを使用して、転送中にトークンが盗まれないように。
  7. クライアント側のストレージスキームは、XSS、CSRF、およびトークン漏洩からの保護に焦点を当てた脅威モデルに基づいて選択する必要があります。

気に入ったならばコメントを残してくださいね~

... ページビュー
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka