JWT提供了一种非常有趣的方式来表示可以验证和信任的应用程序之间的声明。 我的目标是展示一个小的样本,它使用出色的Nimbus JOSE + JWT库来生成和验证令牌。
总览
进行介绍的最佳地点之一是这里 。 简而言之,要从jwt.io网站的资料中借用,声明将表示为一个编码的json,分为三部分,并用点(。)分隔。
header.payload.signature
标头是json,其中包含用于对内容进行签名的算法类型(在本例中为RSA),然后对内容进行url和Base64编码:
{
"alg": "RS512"
}
负载是一个包含所有声明的json,有保留的声明,但也允许私有声明:
{
"sub": "samplesubject",
"name": "John Doe",
"iss": "sampleissueer",
"admin": true,
"exp": 1451849539
}
这里的“ sub”(主题),“ iss”(发布者)和“ exp”(到期)是保留的权利要求,而“ name”和“ admin”是私人权利要求。 然后对内容进行Base64Url编码。
最后,标头和有效负载一起使用共享密钥或私钥进行签名,并且签名经过Base64 url编码,并使用(。)分隔符附加到令牌中。
生成密钥对
我的样本是基于RSA的样本,因此第一步是生成密钥对。 JWK是一种将密钥存储为JSON表示形式的灵巧方法,Nimbus库对此提供了支持:
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()
}
...
}
给定这个密钥对,可以使用Gson从中生成一个JWK:
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的示例密钥对如下所示:
{
"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
现在我们有了一个很好的示例密钥对,请加载私钥和公钥:
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:
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的使用者可以读取有效负载并使用公共密钥对其进行验证:
val cSignedJWT = SignedJWT.parse(s)
val verifier = new RSASSAVerifier(publicKey)
println(cSignedJWT.verify(verifier))
println(signedJWT.getJWTClaimsSet().getSubject())
结论
该样本完全基于Nimbus JOSE + JWT网站上提供的样本,如果您有兴趣进一步研究此内容,则绝对应参考Nimbus网站。
- 我的样品在这里
翻译自: https://www.javacodegeeks.com/2016/01/jwt-generating-validating-token-samples.html