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



我一直在使用JWT库来解码Json Web Token,并希望切换到Microsoft的官方JWT实现System.IdentityModel.Tokens.Jwt。

文档非常稀疏,所以我很难弄清楚如何完成我一直在用 JWT 库做的事情。 使用 JWT 库,有一个解码方法,该方法采用 base64 编码的 JWT 并将其转换为 JSON,然后可以反序列化。我想使用System.IdentityModel.Tokens.Jwt做类似的事情,但经过相当多的挖掘,无法弄清楚如何。

对于它的价值,我正在从cookie中读取JWT令牌,以便与Google的身份框架一起使用。

任何帮助将不胜感激。

在包中,有一个名为 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的重载。

执行此操作的代码相当复杂,但可以在开发人员示例中名为"ADAL - 本机应用程序到 REST 服务 - 通过浏览器对话框使用 ACS 进行身份验证"的开发人员示例中的 Global.asax.cx 代码(TokenValidationHandler类)中找到,位于

http://code.msdn.microsoft.com/AAL-Native-App-to-REST-de57f2cc

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

SecurityTokenHandler类的一般(即非 JWT 特定)文档位于

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编码/解码,请参阅另一个SO问题,以及wiki和RFC

我在 System.IdentityModel.TokensSystem.IdentityModel.Tokens.Jwt 之间遇到了版本问题,这是 Jwt 版本 5.0.0.0 之后的已知问题。因此,相反,我下载了最新版本的Microsoft.IdentityModel.Tokens - 注意Microsoft - 并且一切正常。这是我制作的一个很好的片段,用于验证和解码自定义生成的 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

相关内容

  • 没有找到相关文章

最新更新