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.KeyPairGeneratorimport java.security.interfaces.{RSAPrivateKey, RSAPublicKey}import com.google.gson.{GsonBuilder, JsonElement, JsonParser}import com.nimbusds.jose.Algorithmimport 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.Dateimport com.nimbusds.jose._import com.nimbusds.jose.crypto._import com.nimbusds.jose.jwk.{JWKSet, RSAKey}import com.nimbusds.jwt.JWTClaimsSet.Builderimport 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. |