使用 System.IdentityModel.Token. JWT 解码和验证 JWT 令牌

我一直在使用 智威汤逊库来解码一个 JsonWebToken,现在想切换到 Microsoft 的官方 JWT 实现 System.IdentityModel.Tokens.Jwt

文档非常稀少,因此我很难确定如何使用 JWT 库完成我所做的工作。对于 JWT 库,有一个 Decode 方法,它接受 base64编码的 JWT 并将其转换为 JSON,然后可以对其进行反序列化。我想做一些类似的使用系统。身份模型。代币。但是经过大量的挖掘,还是不知道是怎么回事。

值得一提的是,我正在从 cookie 中读取 JWT 令牌,以便与 Google 的标识框架一起使用。

如果你能帮忙,我将不胜感激。

127579 次浏览

包中有一个名为 JwtSecurityTokenHandler的类,它派生自 System.IdentityModel.Tokens.SecurityTokenHandler。在 WIF 中,这是反序列化和序列化安全令牌的核心类。

该类有一个 ReadToken(String)方法,该方法接受 base64编码的 JWT 字符串并返回一个表示 JWT 的 SecurityToken

SecurityTokenHandler还有一个 ValidateToken(SecurityToken)方法,它接受您的 SecurityToken并创建一个 ReadOnlyCollection<ClaimsIdentity>。通常对于 JWT,这将包含一个 ClaimsIdentity对象,该对象具有一组表示原始 JWT 属性的声明。

JwtSecurityTokenHandlerValidateToken定义了一些额外的重载,特别是它有一个 ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)重载。TokenValidationParameters参数允许您指定令牌签名证书(作为 X509SecurityTokens的列表)。它还有一个重载,将 JWT 作为 string而不是 SecurityToken

这样做的代码相当复杂,但可以在 Global.asax.cx 代码(TokenValidationHandler类)的开发人员示例中找到,该示例名为“ ADAL-NativeApp to REST service-Authentication with ACS via Browser Dialog”,位于

Http://code.msdn.microsoft.com/aal-native-app-to-rest-de57f2cc

另外,JwtSecurityToken类还有其他不在基 SecurityToken类上的方法,例如 Claims属性,该属性不通过 ClaimsIdentity集合获取所包含的声明。它还有一个返回 JwtPayload对象的 Payload属性,该对象允许您获取令牌的原始 JSON。这取决于您的场景中哪种方法最合适。

The general (i.e. non JWT specific) documentation for the SecurityTokenHandler class is at

Http://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.securitytokenhandler.aspx

根据应用程序的不同,您可以像配置其他处理程序一样将 JWT 处理程序配置到 WIF 管道中。

有3个样本在不同类型的应用程序中使用

Http://code.msdn.microsoft.com/site/search?f%5b0%5d.type=searchtext&f%5b0%5d.value=aal&f%5b1%5d.type=user&f%5b1%5d.value=azure%20ad%20developer%20experience%20team&f%5b1%5d.text=azure%20ad%20developer%20experience%20team

很可能,一个可以满足您的需求,或者至少可以适应它们。

我只是想知道为什么要使用一些库来进行 JWT 令牌解码和验证。

可以使用 跟随伪代码创建编码的 JWT 令牌

var headers = base64URLencode(myHeaders);
var claims = base64URLencode(myClaims);
var payload = header + "." + claims;


var signature = base64URLencode(HMACSHA256(payload, secret));


var encodedJWT = payload + "." + signature;

使用以下代码:

using System;
using System.Text;
using System.Security.Cryptography;


public class Program
{
// More info: https://stormpath.com/blog/jwt-the-right-way/
public static void Main()
{
var header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
var claims = "{\"sub\":\"1047986\",\"email\":\"jon.doe@eexample.com\",\"given_name\":\"John\",\"family_name\":\"Doe\",\"primarysid\":\"b521a2af99bfdc65e04010ac1d046ff5\",\"iss\":\"http://example.com\",\"aud\":\"myapp\",\"exp\":1460555281,\"nbf\":1457963281}";


var b64header = Convert.ToBase64String(Encoding.UTF8.GetBytes(header))
.Replace('+', '-')
.Replace('/', '_')
.Replace("=", "");
var b64claims = Convert.ToBase64String(Encoding.UTF8.GetBytes(claims))
.Replace('+', '-')
.Replace('/', '_')
.Replace("=", "");


var payload = b64header + "." + b64claims;
Console.WriteLine("JWT without sig:    " + payload);


byte[] key = Convert.FromBase64String("mPorwQB8kMDNQeeYO35KOrMMFn6rFVmbIohBphJPnp4=");
byte[] message = Encoding.UTF8.GetBytes(payload);


string sig = Convert.ToBase64String(HashHMAC(key, message))
.Replace('+', '-')
.Replace('/', '_')
.Replace("=", "");


Console.WriteLine("JWT with signature: " + payload + "." + sig);
}


private static byte[] HashHMAC(byte[] key, byte[] message)
{
var hash = new HMACSHA256(key);
return hash.ComputeHash(message);
}
}

令牌解码是上述代码的反向版本。要验证签名,您需要使用相同的方法,并将签名部分与计算签名进行比较。

更新: 对于那些如何挣扎如何做 base64 urlsafe 编码/解码请参阅另一个 有个问题,还有 wiki 和 RFC

我在 System.IdentityModel.TokensSystem.IdentityModel.Tokens.Jwt之间有版本问题,这是 Jwt 版本5.0.0.0之后的一个已知问题。因此,我下载了 Microsoft.IdentityModel.Tokens的最新版本-注意 微软-一切都很好。下面是我为验证和解码定制生成的 JWT 令牌并解析其 JSON 内容而制作的一个很好的代码片段。

using System.Collections.Generic;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;


public static void Main()
{
var key = "qwertyuiopasdfghjklzxcvbnm123456";
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(key));


string token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NDA0MDY1MjIsImV4cCI6MTY3MTk0MjUyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsImZvbyI6ImJhciJ9.QqcxZWEUt5YLraLRg5550Ls7aMVqm7aCUcbU7uB1qgY";


TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = securityKey,
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidIssuer = "Online JWT Builder",
ValidAudience = "www.example.com"
};


if (ValidateToken(token, tokenValidationParameters))
{
var TokenInfo = new Dictionary<string, string>();
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);
var claims = jwtSecurityToken.Claims.ToList();


foreach (var claim in claims)
{
TokenInfo.Add(claim.Type, claim.Value);
}


string sub = jwtSecurityToken.Subject;
string iss = jwtSecurityToken.Issuer;
DateTime iat = jwtSecurityToken.IssuedAt;
List<string> audiences = new List<string>(jwtSecurityToken.Audiences);
DateTime exp = jwtSecurityToken.ValidTo;
string bar;
bool ifBar = TokenInfo.TryGetValue("foo", out bar);
Console.WriteLine("Subject: " + sub);
Console.WriteLine("Issuer: " + iss);
Console.WriteLine("Issued At: " + iat);
foreach (var member in audiences)
{
Console.WriteLine("Audience: " + member);
}
Console.WriteLine("Expiration: " + exp);
Console.WriteLine("foo: " + bar);
}
Console.ReadLine();
}


private static bool ValidateToken(string token, TokenValidationParameters tvp)
{
try
{
var handler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
ClaimsPrincipal principal = handler.ValidateToken(token, tvp, out securityToken);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}

输出

Subject: jrocket@example.com
Issuer: Online JWT Builder
Issued At: 12/25/2022 4:28:42 AM
Audience: www.example.com
Expiration: 12/25/2022 4:28:42 AM
foo: bar