Статьи

JWT — Генерация и проверка токена — Образцы

JWT предоставляет очень интересный способ представления заявок между приложениями, которые можно проверять и доверять. Моя цель здесь — показать небольшой пример для генерации и проверки токена с использованием превосходной библиотеки Nimbus JOSE + JWT .

обзор

Одно из лучших мест для знакомства — здесь . Вкратце, заимствуя материал из сайта jwt.io, заявки представлены в виде закодированного json в трех частях, разделенных точкой (.)

1
header.payload.signature

Заголовок — это json, который содержит тип алгоритма, используемого для подписи контента (в данном случае RSA), который затем кодируется по URL и Base64:

1
2
3
{
  "alg": "RS512"
}

Полезная нагрузка — это json, содержащий все заявки, есть заявки, которые зарезервированы, но частные заявки также разрешены

1
2
3
4
5
6
7
{
  "sub": "samplesubject",
  "name": "John Doe",
  "iss": "sampleissueer",
  "admin": true,
  "exp": 1451849539
}

здесь «sub» (субъект), «iss» (эмитент) и «exp» (expiry) являются зарезервированными утверждениями, а «name» и «admin» являются частными утверждениями. Затем содержимое кодируется Base64Url.

Наконец, заголовок и полезная нагрузка вместе подписываются с использованием общего ключа или личного ключа, и подпись кодируется с помощью Base64 url ​​и добавляется к токену с разделителем (.).

Генерация пары ключей

Мой пример основан на RSA, поэтому первым шагом является генерация пары ключей. JWK — это удобный способ хранить ключи как представление JSON, и библиотека Nimbus обеспечивает это:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.security.KeyPairGenerator
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
 
import com.google.gson.{GsonBuilder, JsonElement, JsonParser}
import com.nimbusds.jose.Algorithm
import com.nimbusds.jose.jwk.{JWKSet, KeyUse, RSAKey}
 
object JWKGenerator {
 
  def make(keySize: Integer, keyUse: KeyUse, keyAlg: Algorithm, keyId: String) = {
    val generator = KeyPairGenerator.getInstance("RSA")
    generator.initialize(keySize)
    val kp = generator.generateKeyPair()
    val publicKey = kp.getPublic().asInstanceOf[RSAPublicKey]
    val privateKey = kp.getPrivate().asInstanceOf[RSAPrivateKey]
    new RSAKey.Builder(publicKey)
      .privateKey(privateKey)
      .keyUse(keyUse)
      .algorithm(keyAlg)
      .keyID(keyId)
      .build()
  }
 ...
 
}

Учитывая эту пару ключей, JWK может быть сгенерирован из этого с помощью Gson :

01
02
03
04
05
06
07
08
09
10
def generateJWKKeypair(rsaKey: RSAKey): JsonElement = {
    val jwkSet = new JWKSet(rsaKey)
    new JsonParser().parse(jwkSet.toJSONObject(false).toJSONString)
  }
 
  def generateJWKJson(rsaKey: RSAKey): String = {
    val jsonElement  = generateJWKKeypair(rsaKey)
    val gson = new GsonBuilder().setPrettyPrinting().create()
    gson.toJson(jsonElement)
  }

Пример пары ключей на основе JWK выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
{
  "keys": [
    {
      "p": "2_Fb6K50ayAsnnQl55pPegE_JNTeAjpDo9HThZPp6daX7Cm2s2fShtWuM8JBv42qelKIrypAAVOedLCM75VoRQ",
      "kty": "RSA",
      "q": "ye5BeGtkx_9z3V4ImX2Pfljhye7QT2rMhO8chMcCGI4JGMsaDBGUmGz56MHvWIlcqBcYbPXIWORidtMPdzp1wQ",
      "d": "gSjAIty6uDAm8ZjEHUU4wsJ8VVSJInk9iR2BSKVAAxJUQUrCVN---DKLr7tCKgWH0zlV0DjGtrfy7pO-5tcurKkK59489mOD4-1kYvnqSZmHC_zF9IrCyZWpOiHnI5VnJEeNwRz7EU8y47NjpUHWIaLl_Qsu6gOiku41Vpb14QE",
      "e": "AQAB",
      "use": "sig",
      "kid": "sample",
      "qi": "0bbcYShpGL4XNhBVrMI8fKUpUw1bWghgoyp4XeZe-EZ-wsc43REE6ZItCe1B3u14RKU2J2G57Mi9f_gGIP_FqQ",
      "dp": "O_qF5d4tQUl04YErFQ2vvsW4QoMKR_E7oOEHndXIZExxAaYefK5DayG6b8L5yxMG-nSncZ1D9ximjYvX4z4LQQ",
      "alg": "RS512",
      "dq": "jCy-eg9i-IrWLZc3NQW6dKTSqFEFffvPWYB7NZjIVa9TlUh4HmSd2Gnd2bu2oKlKDs1pgUnk-AAicgX1uHh2gQ",
      "n": "rX0zzOEJOTtv7h39VbRBoLPQ4dRutCiRn5wnd73Z1gF_QBXYkrafKIIvSUcJbMLAozRn6suVXCd8cVivYoq5hkAmcRiy0v7C4VuB1_Fou7HHoi2ISbwlv-kiZwTmXCn9YSHDBVivCwfMI87L2143ZfYUcNxNTxPt9nY6HJrtJQU"
    }
  ]
}

Генерация JWT

Теперь, когда у нас есть хороший пример пары ключей, загрузите закрытый и открытый ключи:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import java.time.{LocalDateTime, ZoneOffset}
import java.util.Date
 
import com.nimbusds.jose._
import com.nimbusds.jose.crypto._
import com.nimbusds.jose.jwk.{JWKSet, RSAKey}
import com.nimbusds.jwt.JWTClaimsSet.Builder
import com.nimbusds.jwt._
 
object JwtSample {
  def main(args: Array[String]): Unit = {
    val jwkSet = JWKSet.load(JwtSample.getClass.getResource("/sample.json").toURI.toURL)
    val jwk = jwkSet.getKeyByKeyId("sample").asInstanceOf[RSAKey]
 
    val publicKey = jwk.toRSAPublicKey
    val privateKey = jwk.toRSAPrivateKey
 ...
}

Создайте полезную нагрузку, подпишите ее и сгенерируйте JWT:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
val claimsSetBuilder = new Builder()
      .subject("samplesubject")
      .claim("name", "John Doe")
      .claim("admin", true)
      .issuer("sampleissueer")
      .expirationTime(Date.from(LocalDateTime.now().plusHours(1).toInstant(ZoneOffset.UTC)))
 
    val signer = new RSASSASigner(privateKey)
 
 
    val signedJWT: SignedJWT = new SignedJWT(
      new JWSHeader(JWSAlgorithm.RS512),
      claimsSetBuilder.build())
 
    signedJWT.sign(signer)
 
    val s = signedJWT.serialize()

Потребитель этого JWT может прочитать полезную нагрузку и проверить ее, используя открытый ключ:

1
2
3
4
5
6
val cSignedJWT = SignedJWT.parse(s)
 
    val verifier = new RSASSAVerifier(publicKey)
 
    println(cSignedJWT.verify(verifier))
    println(signedJWT.getJWTClaimsSet().getSubject())

Вывод

Этот образец полностью основан на образцах, представленных на сайте Nimbus JOSE + JWT , вам обязательно следует обратиться к сайту Nimbus, если вы заинтересованы в дальнейшем изучении этого.

Ссылка: JWT — Генерация и проверка токена — Образцы от нашего партнера JCG Биджу Кунджуммена в блоге all and sundry.