C# 中 JWT 原理,如何生成,如何校验

JWT (Json Web Token)原理:

JWT 把登录信息(令牌),保存在客户端,这样可以有效的解决 Session 在分布式集群环境用户认证的问题。

使用服务端自定义的密钥对保存在客户端的令牌进行签名处理,每次服务端接收到客户端提交过来的令牌都需要检查下签名,验证用户身份

JWT 令牌 由三部分组成


头部 Header负载 Payload签名Signature
{“alg”:“hmac-sha256”【算法】,{“Id”:“6”,“Name”:“admin”,HMACSHA256(header+“.”+payload,
“type”:“JWT”}“exp”:“1688342858”}secKey)

(1)其中将用户信息存储到 Payload 中,一般不建议存太多数据,太多数据会加大流量的消耗

(2)在这三部分中,Header 和 Payload 都是明文,在不知道密钥的情况下也是可以将其解析出来的,所以不要将比较重要的信息放到 Payload 中,防止信息泄露。

JWT 令牌 头部、负载、签名 之间分别用 “.” 进行分隔
比如说:我这里的 JWT 令牌为:(其中我在负载中加了个数据 name = “百香果” )

eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoi55m-6aaZ5p6cIiwiZXhwIjoxNjk5NzU3NzQ3fQ.QJ6YXJAvhbaL34NotLYt7EkyZDU87Ib_ovaafhPWcN8

而通过下面的代码就可以将其头部和 负载中的信息解析出来

//解析代码
string JwtDecode(string s)
{
    s = s.Replace('-', '+').Replace('_', '/');
    switch (s.Length % 4)
    {
        case 2:
            s += "==";
            break;
        case 3:
            s += "=";
            break;
    }
    var bytes = Convert.FromBase64String(s);
    return Encoding.UTF8.GetString(bytes);
}
//调用
Program program = new Program();
string[] jwtArr = jwt.Split('.');
Console.WriteLine(jwtArr[0]);//eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9
Console.WriteLine(program.JwtDecode(jwtArr[0]));//{"alg":"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256","typ":"JWT"}
Console.WriteLine(jwtArr[1]);//eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoi55m-6aaZ5p6cIiwiZXhwIjoxNjk5NzU3NzQ3fQ
Console.WriteLine(program.JwtDecode(jwtArr[1]));//{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"百香果","exp":1699757747}
Console.WriteLine(jwtArr[2]);//QJ6YXJAvhbaL34NotLYt7EkyZDU87Ib_ovaafhPWcN8
//只有密钥解码不出来
Console.WriteLine(program.JwtDecode(jwtArr[2]));//@??\?/????h??-?I2d5<?????~?p?

(2)在这里就体现出 密钥 不能暴露的重要性,因为校验需要 头部信息 + 负载 + 密钥
三个方面去进行校验,任何一个不对都校验不成功。

如果传过来的令牌用户信息不对,校验信息肯定不通过,如果令牌在生成时使用的密钥与服务器中的不一致,即使用户信息一致,校验也不通过

JWT 原理示意图

一点要注意在服务端定义的密钥不要被客户端知道:然后在通过算法将 header payload 密钥,给结合在一起反馈回去,如果客户端知道了密钥,就可以造假了

---------------------                                       -------------------服务端--------------------------------------------------------------------------
|                   |       -------传递用户信息过来--------> |  服务端生成JWT:JWT=header.payload(name:admin).签名;                                           |
|                   |       <-------JWT传递给客户端保存----- |签名=签名算法(header+payload+服务端才知道的密钥(secKey))                                      |
|                   |                                        --------------------------------------------------------------------------------------------------
|    客户端         |      ---客户端再次访问将 JWT 传过来--> |1.算签名=签名算法(用户提交的 jwt 的 header + 用户提交的 jwt 的 payload + 服务器端得出的密钥)  |                                               
|                   |                                        |2.比较“算签名”和用户提交的 JWT 中的签名是否一致,如果不一致,报错。                           |                      
|                   |                                        |3.从 payload 中取出用户信息                                                                     |
---------------------                                      ----------------------------------------------------------------------------------------------------

生成 JWT 令牌

(1)Nuget: Install-Package System.IdentityModel.Tokens.Jwt
(2)调用下面的代码生成 JWT

//在这里写一下 JWT 生成令牌的代码---------------------,再具体的看文档
//1.生成一个 Claim 集合, 一般来讲,再该集合中不需要加这么多,会耗费流量
 List<Claim> claims = new List<Claim>();
 //2.在 Claim 集合中加入对应的信息
 claims.Add(new Claim(ClaimTypes.Name, "百香果"));
 //claims.Add(new Claim(ClaimTypes.Email, "bxg@bxg.com"));
 //claims.Add(new Claim(ClaimTypes.NameIdentifier, "888888"));
 //claims.Add(new Claim(ClaimTypes.HomePhone, "88888888888"));
 可以为其指定多个角色信息
 //claims.Add(new Claim(ClaimTypes.Role, "admin"));
 //claims.Add(new Claim(ClaimTypes.Role, "manager"));
 也可以自定义名字,但是只要解析方通过我们自定义的名字解析即可
 //claims.Add(new Claim("QQ", "88888888888"));
 //claims.Add(new Claim("WeiChart", "888888888"));
 //设置在服务端的密钥:该密钥不要暴露
 //这里只是演示,正常情况下,密钥应该保存在配置中,防止别人读取到
 string key = "dq1oi314313j1esdf45455dw1sdq534*&8s4q>,__9214";
 //设置过期时间,意思是发给你的 jwt 什么时候过期
 DateTime expire = DateTime.Now.AddHours(1);//这里设置一小时后过期
 //3.剩下的代码是生成 JWT 的
 byte[] secBytes = Encoding.UTF8.GetBytes(key);
 var secKey = new SymmetricSecurityKey(secBytes);
 var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);
 var tokenDescriptor = new JwtSecurityToken(claims: claims,
     expires: expire, signingCredentials: credentials);
 string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);

校验 JWT 令牌

将 JWT 令牌保存客户端,当再次访问时带着 JWT 令牌一起访问,通过下面的代码进行校验

//这里的 Key 需要和生成 JWT 时的一样,同理需要保存好,最好保存在一个配置文件中,
//生成 JWT 令牌时取该配置文件,校验 JWT 令牌时取该配置文件
string key = "dq1oi314313j1esdf45455dw1sdq534*&8s4q>,__9214";
JwtSecurityTokenHandler tokenHandler = new();
TokenValidationParameters valParam = new();
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
valParam.IssuerSigningKey = securityKey;
valParam.ValidateIssuer = false;
valParam.ValidateAudience = false;
//这里解析如果密钥不对,就会抛异常,如果密钥对了,前面的payload 或者 header 信息被改了,也会抛异常
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt,
            valParam, out SecurityToken secToken);
//没问题就可以解析出来
foreach (var claim in claimsPrincipal.Claims)
{
    Console.WriteLine($"{claim.Type}={claim.Value}");
}

JWT 的优点

JWT 的优点:
1.状态保存在客户端,而非服务器端,天然适合分布式系统
2.签名保证了客户端无法数据造假
3.性能更高,不需要和中心状态服务器通讯(使用 Session 在分布式集群环境中可以通过将数据保存到中心服务器时,校验的时候取,但这样性能不高),纯内存的计算

好的,我可以帮助你生成一个JWT生成校验的代码。 首先,你需要安装 JSON Web Token(JWT)的依赖。在 Node.js ,你可以使用以下命令安装它: ``` npm install jsonwebtoken ``` 然后,你可以使用以下代码生成一个JWT: ```javascript const jwt = require('jsonwebtoken'); const payload = { // 这里是 JWT 里面的 payload 数据 userId: 123, username: 'john.doe', }; const secret = 'your-secret-key'; const token = jwt.sign(payload, secret, { expiresIn: '1h' }); console.log(token); ``` 在这个代码,我们首先定义了一个 `payload` 对象,里面包含了一些与用户有关的数据。然后,我们定义了一个 `secret`,用于加密 JWT。 最后,我们使用 `jwt.sign` 方法生成 JWT,并将其打印到控制台上。 生成 JWT 后,你还可以根据需要对其进行校验。以下是一个简单的校验代码: ```javascript const jwt = require('jsonwebtoken'); const token = 'your-jwt-token'; const secret = 'your-secret-key'; try { const decoded = jwt.verify(token, secret); console.log(decoded); } catch (e) { console.error(e); } ``` 在这个代码,我们首先定义了一个 `token`,它是我们之前生成JWT。然后,我们使用 `jwt.verify` 方法对其进行校验。 如果校验成功,`jwt.verify` 方法将返回 JWT 的 `payload`,你可以在代码进一步处理 `payload`。否则,它将抛出一个异常,您可以在 catch 语句捕获并处理它。 希望这些代码可以帮助您解决问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值