JJWT(Java JWT)は、Java環境でJSON Web Token(RFC 7519)を扱うための、直感的で強力なライブラリです。デジタル署名(JWS)や暗号化(JWE)などの仕様をサポートしており、マイクロサービス間の認証やセッション管理に広く利用されています。
Maven依存関係のセットアップ
プロジェクトでJJWTを利用するには、pom.xmlに以下の依存関係を追加します。APIインターフェース、実装、およびJSON処理用のJackson実装を定義します。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
JWTの生成(署名付き)
HMAC-SHAアルゴリズムを使用して、秘密鍵による署名付きトークンを発行する基本的な方法です。
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
// 署名用の秘密鍵を生成
SecretKey signingKey = Jwts.SIG.HS256.key().build();
// トークンの構築
String jwtToken = Jwts.builder()
.subject("member_456")
.claim("role", "editor")
.claim("email", "dev@example.com")
.issuedAt(new Date())
.signWith(signingKey)
.compact();
JWTの検証とパース
受け取ったトークンを検証し、ペイロードから特定のクレーム(情報)を取得する処理です。
try {
var claims = Jwts.parser()
.verifyWith(signingKey)
.build()
.parseSignedClaims(jwtToken)
.getPayload();
String userId = claims.getSubject();
String userRole = claims.get("role", String.class);
System.out.println("User ID: " + userId);
} catch (io.jsonwebtoken.JwtException e) {
// 署名不正や改ざん、有効期限切れのハンドリング
System.err.println("Invalid Token: " + e.getMessage());
}
有効期限の設定
セキュリティを担保するため、トークンには必ず有効期限を設定する必要があります。Java 8のTime APIを利用した設定例です。
import java.time.Instant;
import java.time.temporal.ChronoUnit;
Instant currentTime = Instant.now();
Instant expireTime = currentTime.plus(30, ChronoUnit.MINUTES);
String shortLivedToken = Jwts.builder()
.issuedAt(Date.from(currentTime))
.expiration(Date.from(expireTime))
.subject("auth_user")
.signWith(signingKey)
.compact();
RSA非対称鍵による署名
公開鍵と秘密鍵を使用して、署名と検証を行う方法です。認証サーバーが秘密鍵で署名し、リソースサーバーが公開鍵で検証する場合に適しています。
import java.security.KeyPair;
// RSA鍵ペアの生成
KeyPair pair = Jwts.SIG.RS256.keyPair().build();
// 秘密鍵で署名
String rsaSignedJwt = Jwts.builder()
.subject("secure_api_access")
.signWith(pair.getPrivate())
.compact();
// 公開鍵で検証
var parsedData = Jwts.parser()
.verifyWith(pair.getPublic())
.build()
.parseSignedClaims(rsaSignedJwt);
主なクレーム設定メソッド
issuer(String): トークンの発行者を識別する文字列。audience(String): トークンの利用対象となる受信者。notBefore(Date): この時刻より前にはトークンを有効とみなさない。id(String): トークンの一意識別子(JTI)。リプレイ攻撃対策に使用。
例外クラスの分類
JJWTでは、検証失敗の原因に応じて特定の例外がスローされます。
ExpiredJwtException: 有効期限が切れている場合。MalformedJwtException: JWTの構造(ヘッダー、ペイロード、署名の区切り等)が壊れている場合。SignatureException: 署名の不一致(改ざんの可能性)がある場合。UnsupportedJwtException: ライブラリがサポートしていない形式やアルゴリズムの場合。
運用上の留意点
- 機密情報の扱い: ペイロードはBase64Urlでエンコードされているだけであり、誰でもデコード可能です。パスワードなどの機密情報は含めないでください。
- 適切なアルゴリズムの選択: 要件に応じてHS256(共通鍵)やRS256(公開鍵)を選択し、鍵の長さも十分な強度を確保してください。
- トランスポート層の保護: トークンの盗聴を防ぐため、常にHTTPS経由で転送してください。
- 鍵のローテーション: 万が一の漏洩に備え、署名鍵は定期的に更新する仕組みを導入することが推奨されます。