菜单栏

在 .NET Core 中生成和解析JWT令牌(2):使用 HS256(HMAC-SHA256)算法签名

@iEricLee 创建时间 : 2024-09-11 11:06:03 最后修改时间 : 2024-09-11 12:47:54

在前面我们已经完成了 在 .NET Core 中生成和解析JWT令牌(1):基础知识和准备工作 ,本文中将通过示例演示使用 HS256(HMAC-SHA256)签名算法创建JWT令牌

为了方便代码管理,将这个示例的核心代码封装在一个单独的类 Jwt_HMACSHA256Helper 中。

/// <summary>
/// JwtToken生成、校验(HMACSHA256)
/// </summary>
public static class Jwt_HMACSHA256Helper
{
    //密钥
    private static string KEY = "f47b558d-7654-458c-99f2-123456ef0199";
    //todo
}

使用 HS256(HMAC-SHA256)签名算法生成JWT令牌

接下来我们实现生成JWT令牌的方法。

/// <summary>
/// 生成token
/// </summary>
/// <returns></returns>
public static string GetJwtToken()
{
    //对称秘钥
    SecurityKey securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KEY));

    var claims = new List<Claim>() {
        new Claim("ID",Guid.NewGuid().ToString()),
        new Claim("Name","ZhiShiLe")
    };

    JwtSecurityToken jwtToken = new JwtSecurityToken(
        issuer: "auth.zhishile.com",
        audience: "ZhiShiLe.Client",
        claims: claims,
        notBefore: DateTime.UtcNow,
        expires: DateTime.UtcNow.AddSeconds(10),
        signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
    );

    string token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
    return token;
}

签名凭据 SigningCredentials 构造函数接收两个参数:密钥和算法,这里使用对称密钥 SymmetricSecurityKeySecurityAlgorithms.HmacSha256 算法。

Program.cs 中调用 GetJwtToken() 方法,并打印出生成的令牌。

var token = Jwt_HMACSHA256Helper.GetJwtToken();
Console.WriteLine(token);

执行 dotnet run 运行程序,并打印出生成的令牌。

我们得到以下输出结果,一个通过 HS256 算法进行签名的JWT令牌:(以实际运行结果为准)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6IjdiMGYzOTlmLWI2NWUtNGE4Mi1iY2RlLWM0MzkyNDZlNDFkNiIsIk5hbWUiOiJaaGlTaGlMZSIsIm5iZiI6MTcyNDIzNzY1MCwiZXhwIjoxNzI0MjM3NjYwLCJpc3MiOiJhdXRoLnpoaXNoaWxlLmNvbSIsImF1ZCI6IlpoaVNoaUxlLkNsaWVudCJ9.rBvn8IwTvoUPmPQvDt661B7pW-AyuR2XVZ7e_SAaVRY

手动校验和解析令牌

根据JWT格式规范,生成的令牌是一串字符串,由 . 分隔的三部分组成:HEADER.PAYLOAD.SIGNATURE

其中第三部分 SIGNATURE 是签名,用于校验,防止数据被篡改。

接下来,我们使用 https://jwt.io 手动校验和解析令牌。

如下图所示,将令牌内容粘贴到网站中,正确解析出 Header 和 Payload 内容。

jwt_token_01

将密钥粘贴到 VERIFY SIGNATURE 输入框中,显示 Verify Signature ,表示验证签名成功。

jwt_token_02

代码校验和解析令牌

手动验证可以快速查看令牌内容和验证密钥签名是否正确,但是通常我们需要在程序中对令牌进行校验和解析。

Jwt_HMACSHA256Helper 类中添加一个 VerifyJwtToken 方法,用于校验和解析令牌,接收令牌内容,返回校验结果和一个 ClaimsPrincipal 实例。

/// <summary>
/// 校验token
/// </summary>
/// <param name="token"></param>
/// <param name="principal"></param>
/// <returns></returns>
public static bool VerifyJwtToken(string token, out ClaimsPrincipal principal)
{

}

解析令牌

JwtSecurityTokenHandler 提供直接解析JWT字符串返回 JwtSecurityToken 实例的方法:

var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);

校验令牌

JwtSecurityTokenHandler 还提供了校验令牌的方法 JwtSecurityTokenHandler.ValidateToken ,接收3个参数,返回 ClaimsPrincipal 实例。

第一个参数是令牌内容,一个字符串;第二个参数是令牌验证参数 TokenValidationParameters 用于设置令牌校验参数,包括:

  • ValidateLifetime 是否验证生命周期。
  • ValidateAudience 是否验证接收者。
  • ValidateIssuer 是否验证颁发者。
  • ValidateIssuerSigningKey 是否验证签名。
  • ValidIssuer 获取或设置一个字符串,该字符串表示将用于检查令牌的颁发者的有效颁发者。默认值为null。
  • ValidAudience 获取或设置一个字符串,该字符串表示将用于检查令牌受众的有效受众。默认值为null。
  • IssuerSigningKey 获取或设置用于签名验证的密钥。
  • ClockSkew 获取或设置验证时间时应用的时钟偏差。

注意:要将密钥转换为 ASCII 编码,构建一个 SecurityKey 实例。

通常我们希望直接返回 SecurityToken 实例,通过第三个参数返回,这里用到 out 关键字。

principal = null;
SecurityToken validatedToken = null;
//秘钥
SecurityKey securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KEY));
//校验token
var validateParameter = new TokenValidationParameters()
{
    ValidateLifetime = true,
    ValidateAudience = true,
    ValidateIssuer = true,
    ValidateIssuerSigningKey = true,
    ValidIssuer = Issuer,
    ValidAudience = Audience,
    IssuerSigningKey = securityKey,
    ClockSkew = TimeSpan.Zero //校验过期时间设置此属性
};
try
{
    //校验并解析token,validatedToken是解密后的对象
    principal = new JwtSecurityTokenHandler().ValidateToken(token, validateParameter, out validatedToken);
    //获取payload中的数据
    //var jwtPayload = ((JwtSecurityToken)validatedToken).Payload.SerializeToJson();
}
catch (SecurityTokenExpiredException ex)
{
    //表示过期
}
catch (SecurityTokenException ex)
{
    //表示token错误
}
catch (Exception ex)
{
    //其他异常
}
return validatedToken;

由于输入参数 token 是字符串,并且不能确保用户输入的字符串是完全合法的 JWT令牌内容,因此在对 token 进行验证的同时,使用 try...catch 进行异常处理。


JWT
在本文档中
Copyright © 2024 知识乐 湘ICP备2022022129号-1