如何使用 JwtSecurityTokenHandler 和 JWKS 端点验证 JWT?
How do I validate a JWT using JwtSecurityTokenHandler and a JWKS endpoint?
我正在设计使用 IdentityServer4 来保护多个服务的原型,但需要注意的是,这些服务可能不会迁移(在可预见的未来)以使用 ASP.NET Core 的 OWIN 中间件习惯用法。因此,我无法通过简单地提供众所周知的 IdentityServer 的 JWKS 端点等来利用许多自动验证 JWT 的中间件助手。
如果我能重建这种行为就好了,如果可能的话,我想利用 Microsoft 的 JwtSecurityTokenHandler
实现。但是,我不知道如何利用 IdentityServer 的发现端点提供的 JsonWebKeySet
和 JsonWebKey
类型来提取密钥并执行验证。
JwtSecurityTokenHandler
使用 TokenValidationParameters
to validate a JWT, and those parameters require an instance of one or more SecurityKey
个对象来执行验证。
ClaimsPrincipal ValidateJwt(string token, IdentityModel.Client.DiscoveryResponse discovery)
{
JwtSecurityToken jwt = new JwtSecurityToken(token);
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
ValidIssuer = "expected-issuer",
ValidAudience = "expected-audience",
IssuerSigningKeys = discovery.KeySet.Keys /* not quite */
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
SecurityToken validatedToken;
return handler.ValidateToken(jwt, validationParameters, out validatedToken);
}
如何执行从 JsonWebKeySet
到 IEnumerable<SecurityKey>
的必要转换以便进行验证?是否有另一种方法(除了 OWIN 中间件)也可以使用上面的 DiscoveryResponse
数据?
(遗憾的是,System.IdentityModel.Tokens.Jwt
的文档不是最新的。)
检查此示例:
它从 JWK 手动检索密钥并填充验证参数。
var jwks = "{ keys: [..." // your jwks json string
var signingKeys = new JsonWebKeySet(jwks).GetSigningKeys();
然后只需将它分配给您的 TokenValidationParameters
的 IssuerSigningKeys
属性。
如果您从 Web 服务读取 jwks,那么您首先需要一个 http 客户端来读取它。
使用包中的代码:
IdentityModel
System.IdentityModel.Tokens.Jwt
进口:
open IdentityModel
open IdentityModel.Client
open System.Security.Cryptography
open System.IdentityModel.Tokens.Jwt
open Microsoft.IdentityModel.Tokens
从 JsonWebKey
到 SecurityKey
的转换:
module JsonWebKey =
let toSecurityKey (webKey : Jwk.JsonWebKey) =
let e = Base64Url.Decode(webKey.E)
let n = Base64Url.Decode(webKey.N)
let mutable rsap = RSAParameters()
rsap.Exponent <- e
rsap.Modulus <- n
let key = RsaSecurityKey(rsap)
key.KeyId <- webKey.Kid
key :> SecurityKey
然后验证令牌:
task {
let cache = new DiscoveryCache(oidConfigUrl)
let! ddr = cache.GetAsync()
let tokenHandler = JwtSecurityTokenHandler()
let validationParameters = TokenValidationParameters()
let issuerSigningKeys =
ddr.KeySet.Keys
|> Seq.map JsonWebKey.toSecurityKey
|> Seq.toList
validationParameters.IssuerSigningKeys <- issuerSigningKeys
validationParameters.ValidIssuer <- ddr.Issuer
validationParameters.ValidAudience <- "account"
validationParameters.RequireSignedTokens <- true
validationParameters.ValidateIssuer <- true
validationParameters.ValidateLifetime <- true
validationParameters.ValidateAudience <- true
let! validationResult =
tokenHandler.ValidateTokenAsync(accessToken, validationParameters)
printfn $"IsValid: {validationResult.IsValid}"
printfn $"Exception: {validationResult.Exception}"
printfn $"Claims: {validationResult.Claims |> Seq.toList}"
printfn $"ClaimsIdentity: {validationResult.ClaimsIdentity.ToString()}"
}
我正在设计使用 IdentityServer4 来保护多个服务的原型,但需要注意的是,这些服务可能不会迁移(在可预见的未来)以使用 ASP.NET Core 的 OWIN 中间件习惯用法。因此,我无法通过简单地提供众所周知的 IdentityServer 的 JWKS 端点等来利用许多自动验证 JWT 的中间件助手。
如果我能重建这种行为就好了,如果可能的话,我想利用 Microsoft 的 JwtSecurityTokenHandler
实现。但是,我不知道如何利用 IdentityServer 的发现端点提供的 JsonWebKeySet
和 JsonWebKey
类型来提取密钥并执行验证。
JwtSecurityTokenHandler
使用 TokenValidationParameters
to validate a JWT, and those parameters require an instance of one or more SecurityKey
个对象来执行验证。
ClaimsPrincipal ValidateJwt(string token, IdentityModel.Client.DiscoveryResponse discovery)
{
JwtSecurityToken jwt = new JwtSecurityToken(token);
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
ValidIssuer = "expected-issuer",
ValidAudience = "expected-audience",
IssuerSigningKeys = discovery.KeySet.Keys /* not quite */
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
SecurityToken validatedToken;
return handler.ValidateToken(jwt, validationParameters, out validatedToken);
}
如何执行从 JsonWebKeySet
到 IEnumerable<SecurityKey>
的必要转换以便进行验证?是否有另一种方法(除了 OWIN 中间件)也可以使用上面的 DiscoveryResponse
数据?
(遗憾的是,System.IdentityModel.Tokens.Jwt
的文档不是最新的。)
检查此示例:
它从 JWK 手动检索密钥并填充验证参数。
var jwks = "{ keys: [..." // your jwks json string
var signingKeys = new JsonWebKeySet(jwks).GetSigningKeys();
然后只需将它分配给您的 TokenValidationParameters
的 IssuerSigningKeys
属性。
如果您从 Web 服务读取 jwks,那么您首先需要一个 http 客户端来读取它。
使用包中的代码:
IdentityModel
System.IdentityModel.Tokens.Jwt
进口:
open IdentityModel
open IdentityModel.Client
open System.Security.Cryptography
open System.IdentityModel.Tokens.Jwt
open Microsoft.IdentityModel.Tokens
从 JsonWebKey
到 SecurityKey
的转换:
module JsonWebKey =
let toSecurityKey (webKey : Jwk.JsonWebKey) =
let e = Base64Url.Decode(webKey.E)
let n = Base64Url.Decode(webKey.N)
let mutable rsap = RSAParameters()
rsap.Exponent <- e
rsap.Modulus <- n
let key = RsaSecurityKey(rsap)
key.KeyId <- webKey.Kid
key :> SecurityKey
然后验证令牌:
task {
let cache = new DiscoveryCache(oidConfigUrl)
let! ddr = cache.GetAsync()
let tokenHandler = JwtSecurityTokenHandler()
let validationParameters = TokenValidationParameters()
let issuerSigningKeys =
ddr.KeySet.Keys
|> Seq.map JsonWebKey.toSecurityKey
|> Seq.toList
validationParameters.IssuerSigningKeys <- issuerSigningKeys
validationParameters.ValidIssuer <- ddr.Issuer
validationParameters.ValidAudience <- "account"
validationParameters.RequireSignedTokens <- true
validationParameters.ValidateIssuer <- true
validationParameters.ValidateLifetime <- true
validationParameters.ValidateAudience <- true
let! validationResult =
tokenHandler.ValidateTokenAsync(accessToken, validationParameters)
printfn $"IsValid: {validationResult.IsValid}"
printfn $"Exception: {validationResult.Exception}"
printfn $"Claims: {validationResult.Claims |> Seq.toList}"
printfn $"ClaimsIdentity: {validationResult.ClaimsIdentity.ToString()}"
}