一、格式
一个标准的 JWT 由三部分组成,用点来间隔,分别是:
- 1.【无加密】Header(头部):描述令牌的类型和签名的算法。
- 2.【无加密】Payload(负载):包含实际的数据(例如角色信息、用户信息等)。
- 3.【通过密钥加密】Signature(签名):用来验证令牌的完整性和来源。
JWT 格式如下:
header.payload.signature
二、原理
当收到一个JWT令牌(由头部、负载和签名三部分组成)时,系统会根据令牌的头部和负载信息,使用预设的密钥和相应的签名算法重新计算签名。然后,将计算得到的签名与令牌中的签名部分进行比对。如果两者一致,则证明令牌未被篡改,鉴权成功。
三、优点
特性 | JWT | 普通Token |
---|---|---|
存储方式 | Token本身包含信息,无需存储在数据库 | Token需存储在数据库中,每次验证需查库 |
性能 | 仅通过签名验证,快速,无需数据库查询 | 每次请求都需查询数据库,可能造成延迟 |
扩展性 | 适用于分布式系统,多个服务器共享 | 需要集中存储,跨多个服务器时较难扩展 |
安全性 | 使用签名验证,难以伪造,且可设定有效期 | 数据库泄露可能导致安全问题 |
灵活性 | 可嵌入自定义数据,如角色、权限等 | 只能存储Token本身,需额外请求数据 |
可用性 | 有效期内可独立验证,适合无状态应用 | 需要依赖数据库验证,适合集中式应用 |
四、代码演示
1.安装Nuget包
System.IdentityModel.Tokens.Jwt
2.生成和解析JWTtoken
class Program
{
public static string Key = "MyJwtDemo";
public static byte[] ByteKey = GetByteKey(Key);
static void Main(string[] args)
{
// 1. 创建JWT Token
string token = GenerateJwtToken("小明", "123456", "管理员");
Console.WriteLine("生成的JWT Token: ");
Console.WriteLine(token);
// 2. 解析JWT Token
ParseJwtToken(token);
}
//获取密钥(长度为256的字节数组形式)
static byte[] GetByteKey(string key)
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] keyBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
return keyBytes;
}
}
// 生成JWT Token
static string GenerateJwtToken(string username, string userid, string role)
{
// 密钥,通常在生产环境中应存储在安全地方
var secretKey = new SymmetricSecurityKey(ByteKey);
var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.Name, username), // 用户名
new Claim(ClaimTypes.NameIdentifier, userid), // 用户id
new Claim(ClaimTypes.Role, role) // 角色
};
var jwtSecurityToken = new JwtSecurityToken(
issuer: "yourIssuer", // 可选,发行者
audience: "yourAudience", // 可选,受众
claims: claims,
expires: DateTime.Now.AddHours(1), // 设置过期时间
signingCredentials: signingCredentials);
var tokenHandler = new JwtSecurityTokenHandler();
string token = tokenHandler.WriteToken(jwtSecurityToken);
return token;
}
// 解析JWT Token
static void ParseJwtToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
// 验证并解析Token
var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(ByteKey)
}, out var validatedToken);
// 提取并打印Claims信息
var jwtToken = (JwtSecurityToken)validatedToken;
Console.WriteLine("Token解析结果: ");
Console.WriteLine($"用户名: {principal.Identity.Name}");
Console.WriteLine($"用户账号: {principal.FindFirst(ClaimTypes.NameIdentifier)?.Value}");
Console.WriteLine($"角色: {principal.FindFirst(ClaimTypes.Role)?.Value}");
}
catch (Exception ex)
{
Console.WriteLine("无效的Token: " + ex.Message);
}
}
}
3.解析头部和负载
namespace JWTTokenDemo
{
class Program
{
static void Main()
{
// JWT Token
string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoi5bCP5piOIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIxMjM0NTYiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiLnrqHnkIblkZgiLCJleHAiOjE3NDYwMDg1NjcsImlzcyI6InlvdXJJc3N1ZXIiLCJhdWQiOiJ5b3VyQXVkaWVuY2UifQ.ZddlswAbFlFE5RrfT4OAnzsSzM0Gx8i3qe8zd467LmE";
// 解析 JWT Token
var parts = token.Split('.');
// 解码 Header 部分
string header = Base64UrlDecode(parts[0]);
Console.WriteLine("Header: " + header);
// 解码 Payload 部分
string payload = Base64UrlDecode(parts[1]);
Console.WriteLine("Payload: " + payload);
}
// Base64Url 解码函数
public static string Base64UrlDecode(string input)
{
string output = input;
output = output.Replace('-', '+'); // 将 Base64Url 的 '-' 替换为 '+'
output = output.Replace('_', '/'); // 将 Base64Url 的 '_' 替换为 '/'
switch (output.Length % 4) // Base64的填充
{
case 2: output += "=="; break;
case 3: output += "="; break;
}
byte[] bytes = Convert.FromBase64String(output);
return Encoding.UTF8.GetString(bytes);
}
}
}
输出结果:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"小明","http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":"123456","http://schemas.microsoft.com/ws/2008/06/identity/claims/role":"管理员","exp":1746008567,"iss":"yourIssuer","aud":"yourAudience"}
注意:上面的1746008567是时间戳,代表的日期是2025-04-30 18:22:47
4.token的校验
namespace JWTTokenDemo
{
class Program
{
static void Main()
{
// JWT Token
string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoi5bCP5piOIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIxMjM0NTYiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiLnrqHnkIblkZgiLCJleHAiOjE3NDYwMDg1NjcsImlzcyI6InlvdXJJc3N1ZXIiLCJhdWQiOiJ5b3VyQXVkaWVuY2UifQ.ZddlswAbFlFE5RrfT4OAnzsSzM0Gx8i3qe8zd467LmE";
string Key = "MyJwtDemo";// 替换为你的密钥
byte[] ByteKey = GetByteKey(Key);
// 解析 JWT Token
var parts = token.Split('.');
string signature = parts[2]; // 这是 JWT 中的签名部分
// 重新计算签名
string data = parts[0] + "." + parts[1]; // 使用头部和负载来计算签名
string calculatedSignature = ComputeHmacSha256Signature(data, ByteKey);
// 验证签名
if (calculatedSignature == signature)
{
Console.WriteLine("签名验证成功");
}
else
{
Console.WriteLine("签名验证失败.");
}
}
//获取密钥(长度为256的字节数组形式)
public static byte[] GetByteKey(string key)
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] keyBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
return keyBytes;
}
}
// Base64Url 解码函数
public static string Base64UrlDecode(string input)
{
string output = input;
output = output.Replace('-', '+'); // 将 Base64Url 的 '-' 替换为 '+'
output = output.Replace('_', '/'); // 将 Base64Url 的 '_' 替换为 '/'
switch (output.Length % 4) // Base64的填充
{
case 2: output += "=="; break;
case 3: output += "="; break;
}
byte[] bytes = Convert.FromBase64String(output);
return Encoding.UTF8.GetString(bytes);
}
// 使用 HMACSHA256 算法计算签名
public static string ComputeHmacSha256Signature(string data, byte[] secretKey)
{
using (HMACSHA256 hmac = new HMACSHA256(secretKey))
{
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
return Base64UrlEncode(hashBytes); // 返回 Base64Url 编码的签名
}
}
// Base64Url 编码函数
public static string Base64UrlEncode(byte[] input)
{
string base64 = Convert.ToBase64String(input);
base64 = base64.Split('=')[0]; // 去掉 Base64 后缀的 '='
base64 = base64.Replace('+', '-'); // 将 Base64 的 '+' 替换为 '-'
base64 = base64.Replace('/', '_'); // 将 Base64 的 '/' 替换为 '_'
return base64;
}
}
}