序文
公式サイトの紹介文の翻訳は以下の通りです。
JWT とは何ですか?
JSON Web Token (JWT) は、関係者間で情報を JSON オブジェクトとして安全に送信するためのコンパクトで自己完結型の方法を定義するオープン標準 ( RFC 7519 ) です。この情報はデジタル署名で行われるため、検証および信頼できます。jwt は、秘密鍵 (HMAC アルゴリズムを使用) または RSA または ECDSA を使用した公開鍵/秘密鍵ペアで署名できます。
JWT は当事者間の機密性を確保するために暗号化することもできますが、トークンの署名に重点を置いてください。署名付きトークンは、その中に含まれるクレームの整合性を検証できますが、暗号化されたトークンは、それらのクレームを他の当事者から隠すことができます。公開キーと秘密キーのペアを使用して署名する場合、その署名は、秘密キーを保持している当事者のみが署名した者であることも証明します。
JWT をいつ使用するか?
承認: これは、JWT を使用する最も一般的なシナリオです。ユーザーがログインすると、後続のすべてのリクエストに JWT が含まれるため、ユーザーはそのトークンで許可されたルート、サービス、リソースにアクセスできるようになります。シングル サインオンは、オーバーヘッドが少なく、さまざまなレルム間で簡単に使用できるため、最近 JWT が広く使用されている機能です。
情報交換: JSON Web トークンは、2 者間で情報を安全に転送するための優れた方法です。JWT は公開鍵と秘密鍵のペアなどを使用して署名できるため、送信者が本人であることを確認できます。また、ヘッダーとペイロードを用いて署名を計算するため、内容が改ざんされていないことも検証できます。
JWT は ID 認証でよく使用され、最も一般的なクロスドメイン認証ソリューションでもあることがわかります。
従来のスタンドアロン バックエンド サービスは、ユーザーのログイン ステータス情報を保存するためにセッションを使用することがよくありますが、このモデルの問題は、WeChat アプレットなどの非ブラウザーに遭遇した場合、ユーザー情報の保存にセッションを使用できないことです。マシン(サーバー クラスター) またはサービス指向のクロスドメインシステムでは、単純なセッションではこの状況に対処できません。
JWT は上記に対する柔軟なソリューションであり、クライアントを通じてデータを保存するステートレストークンですが、サーバーはセッション データをまったく保存せず、各リクエストはサーバーに送り返されます。
JWTの実装ライブラリは多数あり、プログラミング言語ごとに対応ライブラリが存在するため、詳細は公式サイトを参照してください。Java にはいくつかの種類があり、公式 Web サイトで一番上にあるのはオープンソース ライブラリjava-jwtです。
github: https://github.com/auth0/java-jwt
pom の依存関係は次のとおりです。
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.2</version>
</dependency>
1.JWT仕様
JWT はHeader、Payload、Signatureの 3 つの部分で構成されており、3 つの部分はドット(.) で区切られています。
1.1 ヘッダーヘッド
通常、ヘッダーは 2 つの部分で構成されます。1 つはトークンのタイプ (JWT)、もう 1 つは使用されている署名アルゴリズム (HMAC SHA256 や RSA など) です。
{
"alg": "HS256",
"typ": "JWT"
}
JSON データのこの部分は通常、Base64によってJWT のヘッダーとして文字列の文字列にエンコードされます。これは最初の部分であり、js 関数 btoa を通じて実現できます。
btoa('{"alg": "HS256","typ": "JWT"}')
得る
eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0=
1.2 ペイロード
これは JWT の中核部分であり、送信されるデータと、 Claimsと呼ばれるいくつかの定義されたフィールドを含むデータ送信の本体です。
一般的な主張は次のとおりです。
略語 | フルネーム | 意味 |
---|---|---|
です | 発行者 | 発行者 |
サブ | 主題 | 本体 |
オード | 観客 | (受信)対象者 |
経験値 | 有効期限 | 有効期限 |
nbf | 以前はありませんでした | 規定の時間より前のJWT 処理は受け付けられません |
イアット | で発行された | JWT 発行時のタイムスタンプ |
jti | JWT ID | JWT 一意の識別子 |
例:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"exp": 1613297468168
}
また、JWT の 2 番目の部分として文字列として Base64 エンコードされます。
btoa('{"sub": "1234567890","name": "John Doe","admin": true,"exp": 1613297468168}')
得る
eyJzdWIiOiAiMTIzNDU2Nzg5MCIsIm5hbWUiOiAiSm9obiBEb2UiLCJhZG1pbiI6IHRydWUsImV4cCI6IDE2MTMyOTc0NjgxNjh9
1.3 署名 署名
署名の機能は、クレームの整合性 (またはデータ暗号化) を保護し、JWT 送信中にクレームが改ざんされない (またはクラックされない) ことを保証することです。
例: HMAC SHA256 アルゴリズムを使用して、次の方法でヘッダーとペイロードを暗号化し、署名を取得します。ヘッダーとペイロードはいずれもbase64エンコードによる平文で送信され、改ざんされていないことを保証するために署名が使用される 平文部分を取得したとしても、対応する暗号化アルゴリズムはわかっているが、それを理解することは困難秘密鍵を使わずに署名を偽造すること。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
2、Java実装
2.1 作成
-
基本的な使い方
トークンはjava-jwtが提供するAPIを使用して簡単に生成できます。
String userId = "10086"; LocalDateTime now = LocalDateTime.now(); long expireDays = 7; String s= ""; Date startDate = localDateTimeToDate(now); Date endDate = localDateTimeToDate(now.plusDays(expireDays)); Algorithm algorithm = Algorithm.HMAC256(s); Map<String, Object> header = new HashMap<>(); header.put("alg", "HS256"); header.put("typ", "JWT"); String token = JWT.create() .withHeader(header) .withAudience(userId) .withIssuedAt(startDate) .withExpiresAt(endDate) .withClaim("test", "test values") .sign(algorithm); System.out.println(token); // eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxMDA4NiIsInRlc3QiOiJ0ZXN0IHZhbHVlcyIsImV4cCI6MTY3NDU2NzAxMSwiaWF0IjoxNjczOTYyMjExfQ.GSpwAX6WG3RMplu4TdSsYLWTjNlqGh3KC-ifhA-_x50
-
暗号化アルゴリズムを変更する
java-jwt には多くの暗号化アルゴリズムが組み込まれています。例: RSA256 を使用できます。
KeyPairGenerator rsa = KeyPairGenerator.getInstance("RSA"); rsa.initialize(512); KeyPair keyPair = rsa.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); System.out.println(Base64.getEncoder().encodeToString(publicKey.getEncoded())); System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded())); Algorithm rsa256Algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
2.2 復号化
DecodedJWT jwt = JWT.require(algorithm).build().verify(token);
String jwtAlgorithm = jwt.getAlgorithm();
String jwtId = jwt.getAudience().get(0);
Map<String, Claim> claims = jwt.getClaims();
System.out.printf("%s \n%s \n%s%n", jwtAlgorithm, jwtId, claims);
2.3 検証
-
時間チェック
JWT トークンには、それを検証するための日付関連の情報を含めることができます。
- トークンは発行されたトークンです: "iat" < NOW
- トークンの有効期限はまだ切れていません: "exp" > NOW
- トークンはすでに使用されています: "nbf" < NOW
日付の検証はトークンの検証時に自動的に行われ、検証に失敗した場合はJWTVerificationExceptionがスローされます。
検証メソッドのacceptLeewayメソッドはleeway属性を設定するもので、ソースコードを見ると余裕のある2番目の値であることが分かります。
private boolean assertInstantIsFuture(Instant claimVal, long leeway, Instant now) { return claimVal == null || !now.minus(Duration.ofSeconds(leeway)).isAfter(claimVal); } private boolean assertInstantIsPast(Instant claimVal, long leeway, Instant now) { return claimVal == null || !now.plus(Duration.ofSeconds(leeway)).isBefore(claimVal); }
JWTVerifier verifier = JWT.require(algorithm) .acceptLeeway(1) .build(); verifier.verify(token);
-
請求の検証
上記の時間検証に加えて、送信された情報、つまりクレームを検証し、失敗した場合に例外JWTVerificationExceptionをスローすることもできます。
JWTVerifier verifier = JWT.require(algorithm) .acceptLeeway(1) .withClaim("test", "123") .withClaimPresence("test") .build(); verifier.verify(token);