C#开发日记:手把手教你生成JWT

一、JWT(JSON Web Token)简介

JWT(JSON Web Token)是一种用于在网络应用环境间传递声明的一种紧凑的、URL安全的方式。它由三部分组成:Header(头部)、Payload(负载)和Signature(签名)。

一般是user id(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串。为防止token泄露)。

Header(头部)

JWT的头部通常由两部分组成:token的类型(这里是JWT)和所使用的签名算法(这里是HS256,即HMAC-SHA256)。

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg:指定了签名使用的算法,HS256表示使用HMAC-SHA256算法。
  • typ:表示这个token是一个JWT。

Payload(负载)

负载部分包含了所谓的Claims(声明),它们是关于实体(通常是用户)和其他数据的声明。在您提供的token中,负载包含了以下声明:

{
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "string",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid": "1828337手动打码461696",
  "AuthType": "User",
  "nbf": 1724833354,
  "exp": 1724840554,
  "iss": "dlht",
  "aud": "string"
}
  • name:用户的名称或昵称。
  • sid:用户的会话ID或唯一标识符。
  • AuthType:认证类型,这里是"User"。
  • nbf:(Not Before)表示token在此时间之前无效。
  • exp:(Expiration Time)表示token的过期时间。
  • iss:(Issuer)表示token的发行者。
  • aud:(Audience)表示token的接收者。

Signature(签名)

签名用于验证消息在传输过程中未被篡改,并且,对于使用私钥签名的token,还可以验证发送者的身份。签名是使用Base64Url编码的Header、Payload和密钥(Secret)通过指定的算法生成的。

Signature: vlJkgZ5CMszamjDr_DXKsB-9Wu0CsrczZ5yu49t1ZaQ


 

二、配置文件(appsettings.json)

使用Token过滤访问你程序的用户,本次以ASP .NET Core Web应用程序为例,使用SqlSugar的ORM框架,Oracle数据库 ,当然数据库不是主角,无伤大雅!!我的思路是先配置参数后再学你怎么用!

重点使用:Microsoft.IdentityModel.Tokens;

 "JWT_SECRET_KEY": "asldfgjhopq347yr5t9o2qy8fioqasldasweiuedfghd87dasdqa24fhpdeyf870tyq234o87fghawo87yfaow8eyg7hfosgtyfukoawesty7o8",
 "JWT_EXPIRES_SECONDS": "7200"
介绍
JWT_SECRET_KEY密钥
JWT_EXPIRES_SECONDS有效秒数

三、注入依赖(program.cs)

var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddEnvironmentVariables();

...
//注册JwtSercie,并配置JWT
builder.Services.AddScoped<IJwtSercie, JwtSercie>().Configure<JwtSetting>(options =>
{
    options.SecretKey = builder.Configuration["environmentVariables:JWT_SECRET_KEY"];
    options.ExpiresSeconds = int.Parse(builder.Configuration["environmentVariables:JWT_EXPIRES_SECONDS"] ?? throw new ArgumentException("请配置JWT_EXPIRES_SECONDS环境变量"));

...

其实就是把json的两个参数拿出来注入了IServiceCollection,具体是设计了JwtSetting的类型,如下所示:

public class JwtSetting
{
        public string? SecretKey { get; set; }

        public int ExpiresSeconds { get; set; }
    
}

Configure<JwtSetting>()方法按照这个类型注入两个依赖项。

下面模拟用户登录,首先数据库验证账户与密码,成功后返回token。

设计一个简单用户表

ID(雪花)USERNAMEPASSWORDHASH

四、编写服务Service

3.1 JWT服务

GeneratorToken传入一个用户,根据用户信息生成Token(目前我们只有用户名)。

public class JwtSercie(IOptions<JwtSetting> jwtSetting) : IJwtSercie
{
    public string GeneratorToken(Entities.USER_Test user)
    {
        var claims = new[]
        {
         new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 使用用户ID作为NameIdentifier
        //new Claim(ClaimTypes.Name, user!.username!),//使用用户名
        new Claim(ClaimTypes.Sid, user.Id.ToString()),
        new Claim("AuthType", "User"),

    };

        return WriteToken(user.username!, claims);
    }
    private string WriteToken(string audience, Claim[] claims)
    {
        var sourceData = jwtSetting.Value.SecretKey ?? throw new NullReferenceException();
        var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(sourceData));
        const string algorithm = SecurityAlgorithms.HmacSha256;
        var signingCredentials = new SigningCredentials(secretKey, algorithm);
        var jwtSecurityToken = new JwtSecurityToken(
            issuer: "dlht",
            audience: HttpUtility.UrlEncode(audience),
            claims,
            notBefore: DateTime.Now,
            expires: DateTime.Now.AddSeconds(jwtSetting.Value.ExpiresSeconds),
            signingCredentials
        );

        var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        return token;
    }
}

  1. GeneratorToken方法

    • 这个方法接受一个USER_Test类型的用户实体作为参数,并生成一个JWT。
    • 首先,它创建了一个Claim数组,这个数组包含了JWT中要声明的属性。这里包括了用户的用户名(ClaimTypes.Name)、用户ID(ClaimTypes.Sid)和认证类型("AuthType")。
    • 然后,调用WriteToken方法来实际生成JWT。
  2. WriteToken方法

    • 这个方法接受audience(观众,即JWT的接收者)和claims(声明)作为参数。
    • 首先,从jwtSetting中获取SecretKey,如果没有提供则抛出异常。
    • SecretKey转换为SymmetricSecurityKey类型的密钥,这是用于签名JWT的密钥。
    • 定义签名算法为HmacSha256
    • 创建SigningCredentials对象,包含密钥和算法。
    • 创建JwtSecurityToken对象,包含以下属性:
      • issuer(发行者):设置为"dlht"。
      • audience(观众):使用HttpUtility.UrlEncode对观众进行URL编码。
      • claims:之前创建的声明数组。
      • notBefore(生效时间):设置为当前时间。
      • expires(过期时间):当前时间加上jwtSetting中配置的过期秒数。
      • signingCredentials:签名凭据。
    • 使用JwtSecurityTokenHandlerWriteToken方法将JwtSecurityToken对象序列化为字符串形式的JWT。

如果有好好看第一章的话,WriteToken方法便好理解一些。

3.2 用户服务

若想再用户服务中使用JWT服务的方法,需要在构造函数中注入 IJwtSercie 。

 public class UserService([FromKeyedServices("local")] ISqlSugarClient _db, IJwtSercie JwtSercie) : IUserService
 {

     
     public async Task<BaseResponse> UserLogin(string username, string password)
     {
         // 使用SqilSugar查询数据库
         var user = await _db.Queryable<USER_Test>()
             .Where(u => u.username == username && u.PasswordHash == HashPassword(password))
             .FirstAsync();
         if (user == null)
             return new BaseResponse() { Code = -1, Message = "用户名或者密码错误" };
         string token = JwtSercie.GeneratorToken(user ?? throw new NullReferenceException());
         return new BaseResponse() { Code = 1, Data= token,Message="登录成功"};
     }
}

user返回查询用户的信息,若用户存在则将用户传入GeneratorToken中并返回Token

五 编写控制器

[Route("api/[controller]")]
[ApiController]
public class LoginController(IUserService _userService, IAuthenticationService _authenticationService) : ControllerBase
{


    //登录 token
    [HttpPost("[action]")]
    public async Task<IActionResult> Login([FromBody] LoginModel loginModel)
    {
      var result = await _userService.UserLogin(loginModel.Username, loginModel.Password);
       if(result.Code!=1) 
            return Unauthorized(result);
        return Ok(result);
    }
}

六、测试以及分析

JWT的通常遵循Base64Url编码的规则,可能让它看起来没有明显的规律,特别是当它被编码后。让我们可以逐步解析。

例如token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjE4MjgzMzc4MzY1NDY0NjE2OTYiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiIxODI4MzM3ODM2NTQ2NDYxNjk2IiwiQXV0aFR5cGUiOiJVc2VyIiwibmJmIjoxNzI0ODM1MTUwLCJleHAiOjE3MjQ4NDIzNTAsImlzcyI6ImRsaHQiLCJhdWQiOiJzdHJpbmcifQ.4rjl2Bl0OHKnnRQ-yvAw2aoLT48rZt46R-G3tkA6W5E

Header(头部)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

解析后


{
  "alg": "HS256",
  "typ": "JWT"
}

Payload(负载)

eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjE4MjgzMzc4MzY1NDY0NjE2OTYiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiIxODI4MzM3ODM2NTQ2NDYxNjk2IiwiQXV0aFR5cGUiOiJVc2VyIiwibmJmIjoxNzI0ODM1MTUwLCJleHAiOjE3MjQ4NDIzNTAsImlzcyI6ImRsaHQiLCJhdWQiOiJzdHJpbmcifQ

{
  "sub": "42",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

iat声明代表的是“Issued At”,即token的签发时间

Signature(签名)

4rjl2Bl0OHKnnRQ-yvAw2aoLT48rZt46R-G3tkA6W5E

使用Header中指定的算法和密钥生成的签名。

JWT的编码方式是Base64Url编码,它是一种特殊的Base64编码,用于URL和文件名的安全传输。Base64Url编码与标准的Base64编码相比,有以下不同:

  • +替换为-
  • /替换为_
  • 去掉填充字符=

由于Base64Url编码的特性,JWT看起来可能没有明显的规律,特别是当它被编码后。但是,解码后,您可以清楚地看到JWT的每个部分都是JSON格式的,并且包含了必要的声明和元数据。

注册后续

JWT服务注册于控制器的访问授权设置请看下一章——使用生成的JWT设置资源接口验证 http://t.csdnimg.cn/rgU9r

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值