asp.net core 8 JWT的运用
一、什么是JWT
JWT(JSON Web Token)是一种基于 JSON 的轻量级身份验证和信息交换规范,广泛用于分布式系统中的用户身份验证、信息传递等场景。它通过数字签名保证信息的完整性和真实性,无需在服务端存储会话状态,非常适合前后端分离、微服务架构等场景。
一、JWT 的核心优势
- 无状态:服务端无需存储会话信息,减轻服务器负担,便于水平扩展
- 自包含:Token 本身包含用户身份、权限等关键信息,减少数据库查询
- 跨域支持:可在不同域之间安全传递,适合分布式系统
- 结构简单:基于 JSON 格式,易于解析和使用
二、JWT 的结构
JWT 由三部分组成,用.
分隔,格式为:Header.Payload.Signature
例如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiSm9obiBEb2UifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. Header(头部)
-
作用:指定令牌类型(
typ
)和签名算法(alg
) -
格式:JSON 对象,经 Base64Url 编码后形成第一部分
-
示例:
{ "alg": "HS256", // 签名算法(HMAC SHA256) "typ": "JWT" // 令牌类型 }
-
常见签名算法:
HS256
:HMAC-SHA256(对称加密,密钥需保密)RS256
:RSA-SHA256(非对称加密,使用公钥验签)
2. Payload(载荷)
-
作用:存储实际需要传递的数据(声明),包含标准声明和自定义声明
-
格式:JSON 对象,经 Base64Url 编码后形成第二部分
-
标准声明
(建议但不强制):
iss
:签发者(Issuer)exp
:过期时间(Expiration Time,Unix 时间戳)sub
:主题(Subject,通常是用户 ID)aud
:受众(Audience,接收方)iat
:签发时间(Issued At,Unix 时间戳)nbf
:生效时间(Not Before,在此时间前无效)jti
:JWT 唯一标识(用于防止重放攻击)
-
自定义声明:可添加业务相关信息(如用户名、角色、权限等)
-
示例:
{ "sub": "123456789", // 用户ID "name": "John Doe", // 用户名 "role": "admin", // 角色 "iat": 1516239022, // 签发时间 "exp": 1516242622 // 过期时间(1小时后) }
-
注意:Payload 仅经 Base64 编码,不加密,不可存储敏感信息(如密码)
3. Signature(签名)
-
作用:确保 Token 未被篡改,验证发送者身份
-
生成方式:根据 Header 指定的算法,对编码后的 Header、编码后的 Payload 和密钥进行签名
-
计算公式(以 HS256 为例):
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secretKey // 服务器端密钥 )
-
签名验证:接收方用相同算法和密钥重新计算签名,与 Token 中的签名比对,一致则说明 Token 有效
三、JWT 的工作流程
-
用户登录:用户提交用户名 / 密码到认证服务器
-
生成 Token:认证服务器验证通过后,生成 JWT 并返回给客户端
-
客户端存储:客户端(如浏览器)将 Token 存储在 localStorage、sessionStorage 或 Cookie 中
-
请求资源:客户端每次请求需在 HTTP 头部携带 Token,格式:
Authorization: Bearer <token>
-
服务器验证:资源服务器解析 Token,验证签名和过期时间,确认有效后返回资源
四、JWT 的优缺点
优点
- 无状态:服务端无需存储会话,适合分布式系统
- 跨域支持:可在不同服务间传递,简化单点登录(SSO)实现
- 自包含:减少数据库查询,提升性能
缺点
- 无法撤销:Token 一旦签发,在有效期内始终有效(除非服务器维护黑名单)
- payload 不加密:敏感信息不能放在 payload 中
- 过期时间固定:无法动态修改有效期(需重新签发)
五、JWT 的安全最佳实践
- 使用 HTTPS:防止 Token 在传输过程中被窃取
- 设置合理过期时间:短期有效(如 15-30 分钟),降低被盗用风险
- 敏感信息不存 payload:payload 仅 Base64 编码,无加密
- 使用非对称加密:公钥验签,私钥签名,避免密钥泄露风险
- 实现 Token 刷新机制:过期前用刷新令牌(Refresh Token)获取新 Token
- 维护黑名单:对已注销但未过期的 Token 进行拦截
六、JWT 的应用场景
- 前后端分离项目的身份验证
- 微服务架构中的服务间认证
- 单点登录(SSO)系统
- API 接口授权
- 安全的信息传递(如服务间数据交换)
JWT 通过简洁的结构和无状态特性,成为现代 Web 应用中身份验证的重要方案,但需结合安全最佳实践使用,平衡便捷性和安全性。
二、asp.net core 8 中的 JWT组件
System.IdentityModel.Tokens.Jwt
和 Microsoft.AspNetCore.Authentication.JwtBearer
是 ASP.NET Core 中处理 JWT 认证的两个核心组件,但它们的定位和功能截然不同。以下是两者的详细区别:
1. 功能定位
组件 | 核心功能 | 角色 |
---|---|---|
System.IdentityModel.Tokens.Jwt |
提供 JWT 令牌的创建、解析、验证等基础操作(核心算法实现) | 「工具库」:处理 JWT 数据结构本身 |
Microsoft.AspNetCore.Authentication.JwtBearer |
将 JWT 认证集成到 ASP.NET Core 的认证管道中,实现 HTTP 请求的自动验证 | 「中间件」:对接 ASP.NET Core 认证体系 |
2. 核心职责
System.IdentityModel.Tokens.Jwt
- 创建 JWT:通过
JwtSecurityTokenHandler
生成符合 JWT 规范的令牌(包含 Header、Payload、Signature)。 - 解析 JWT:将字符串形式的 Token 解析为
JwtSecurityToken
对象,提取其中的声明(Claims)。 - 验证 JWT:根据签名算法、密钥、过期时间等参数验证 Token 的合法性(底层算法实现)。
- 核心类:
JwtSecurityToken
、JwtSecurityTokenHandler
、JwtRegisteredClaimNames
等。
Microsoft.AspNetCore.Authentication.JwtBearer
- 集成认证管道:作为 ASP.NET Core 的认证中间件,自动拦截 HTTP 请求中的 JWT(通常在
Authorization
头)。 - 自动验证流程:使用
System.IdentityModel.Tokens.Jwt
提供的底层能力,对请求中的 Token 进行验证,并将验证结果转换为 ASP.NET Core 的ClaimsPrincipal
(用户身份)。 - 处理认证结果:验证通过则允许访问受保护资源;失败则返回 401/403 响应。
- 核心类:
JwtBearerOptions
(配置验证参数)、JwtBearerHandler
(中间件处理逻辑)。
3. 依赖关系
Microsoft.AspNetCore.Authentication.JwtBearer
依赖System.IdentityModel.Tokens.Jwt
:前者是 ASP.NET Core 认证体系的 “前端”,负责与 HTTP 管道交互;后者是 “后端”,负责实际的 JWT 解析和验证计算。- 安装
Microsoft.AspNetCore.Authentication.JwtBearer
时,NuGet 会自动安装System.IdentityModel.Tokens.Jwt
作为依赖。
4. 使用场景
何时用 System.IdentityModel.Tokens.Jwt
?
-
生成 JWT 令牌(如用户登录成功后签发 Token)。
-
手动解析或验证 JWT(如非 HTTP 场景下的 Token 处理)。
-
自定义 Token 生成逻辑(如添加复杂声明、自定义签名算法)。
示例(生成 Token):
using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; using System.Text; var handler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes("your-secret-key"); var token = handler.CreateToken(new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("userId", "123") }), Expires = DateTime.UtcNow.AddHours(1), SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256 ) }); string tokenString = handler.WriteToken(token);
何时用 Microsoft.AspNetCore.Authentication.JwtBearer
?
-
在 ASP.NET Core 中保护 API 接口(通过
[Authorize]
特性)。 -
自动验证请求中的 JWT 并生成用户身份(
User
对象)。 -
配置 Token 验证规则(如签发者、受众、过期时间等)。
示例(配置中间件):
// Program.cs 中配置 JWT 认证 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "your-issuer", // 其他验证参数... }; });
5. 总结
System.IdentityModel.Tokens.Jwt
:是处理 JWT 令牌的 “底层工具”,负责 Token 的创建、解析和验证的核心算法。Microsoft.AspNetCore.Authentication.JwtBearer
:是 ASP.NET Core 的 “认证中间件”,负责将 JWT 验证集成到 HTTP 管道,让开发者可以用[Authorize]
轻松保护接口。
三 System.IdentityModel.Tokens.Jwt 创建 JWT
3.1 加密
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
/***
* 创建的结果如下:
{
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "张三",
"QQ": "252100000",
"Id": "00003",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "tangge@vip.qq.com",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "111111",
"exp": 1756727122
}
***/
List<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, "张三"));
claims.Add(new Claim("QQ", "252100000"));
claims.Add(new Claim("Id", "00003"));
claims.Add(new Claim(ClaimTypes.Email, "tangge@vip.qq.com"));
claims.Add(new Claim(ClaimTypes.Role, "Admin"));
claims.Add(new Claim(ClaimTypes.NameIdentifier, "111111")); //这行代码添加了一个表示用户唯一标识符的声明
string key = "amdisfoisdoil092ui3#dfefjei@qde11";
DateTime? expires = DateTime.Now.AddDays(1);
//加密
byte[] keyByte = Encoding.UTF8.GetBytes(key);
//使用字节数组 keyByte 创建一个对称安全密钥对象 secretKey
var secretKey = new SymmetricSecurityKey(keyByte);
//基于前面创建的对称安全密钥 secretKey 和指定的哈希算法 HmacSha256 创建签名凭据对象 credentials
var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
/*
* 创建一个 JWT 安全令牌对象 token。该对象包含三个主要参数:
* claims:这是一个声明集合,通常包含用户的相关信息,如用户 ID、角色等。声明是 JWT 中携带的自定义数据。
* expires:表示 JWT 的过期时间,超过这个时间,JWT 将被视为无效。
* signingCredentials:即前面创建的签名凭据,用于对 JWT 进行签名,以确保其未被篡改。
*/
var token = new JwtSecurityToken(claims: claims, expires: expires,
signingCredentials: credentials);
//使用 JwtSecurityTokenHandler 类的实例将 token 对象转换为字符串形式的 JWT。
string jwt = new JwtSecurityTokenHandler().WriteToken(token);
Console.WriteLine(jwt);
3.2 解密
key = "amdisfoisdoil092ui3#dfefjei@qde11A"; //这里随便写一个签名
//JwtSecurityTokenHandler这个类是 .NET 提供的用于处理 JWT 的工具类,它可以用于创建、解析和验证 JWT
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
//valParam。该实例用于配置 JWT 验证时所需的各种参数,例如签名密钥、是否验证发行者、是否验证受众等
TokenValidationParameters valParam = new TokenValidationParameters();
//基于这个字节数组创建一个对称安全密钥对象 securityKey。对称安全密钥用于验证 JWT 的签名,在对称加密中,加密和解密使用相同的密钥。
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
valParam.IssuerSigningKey = securityKey;
valParam.ValidateIssuer = false; //验证 JWT 时不验证发行者(Issuer)
valParam.ValidateAudience = false; // JWT 时不验证受众(Audience)
//验证成功,会返回一个 ClaimsPrincipal 对象 claimsPrincipal,它包含了 JWT 中的所有声明信息
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt, valParam, out SecurityToken secToken);
foreach (var claim in claimsPrincipal.Claims)
{
Console.WriteLine($"{claim.Type}={claim.Value}");
}
报错:
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException:“IDX10517: Signature validation failed. The token's kid is missing. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: '9IfOFKPfhThi31WvH3150dzAlFjNvoS4cOs4FXB_JR4'.'.
Number of keys in TokenValidationParameters: '1'.
Number of keys in Configuration: '0'.
Exceptions caught:
'[PII of type 'System.String' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. See https://aka.ms/IDX10503 for details.”
如果签名正确,就能输出值:
key = "amdisfoisdoil092ui3#dfefjei@qde11";
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name=张三
QQ=252100000
Id=00003
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress=tangge@vip.qq.com
http://schemas.microsoft.com/ws/2008/06/identity/claims/role=Admin
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier=111111
exp=1756729806
四 asp.net core 8 的封装
配置 JWT 密钥和参数
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Jwt": {
"SecretKey": "YourSuperSuperSecretKeyThatIsAtLeast32BytesLong!", // 密钥(至少32字节)
"Issuer": "Identity", // 签发者
"Audience": "Microservices", // 受众
"ExpiresInMinutes": 30 // Token 有效期
}
}
创建生成 JWT 的服务
创建一个 JwtTokenService
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Identity框架
{
public class JwtTokenService
{
private readonly IConfiguration _configuration;
// 注入配置服务
public JwtTokenService(IConfiguration configuration)
{
_configuration = configuration;
}
// 生成 Token(参数:用户ID、用户名、角色等)
public string GenerateToken(string userId, string userName, IEnumerable<string> roles)
{
var jwtSettings = _configuration.GetSection("Jwt");
var secretKey = jwtSettings["SecretKey"] ?? throw new ArgumentNullException("Jwt:SecretKey is not configured");
var issuer = jwtSettings["Issuer"];
var audience = jwtSettings["Audience"];
var expiresInMinutes = int.Parse(jwtSettings["ExpiresInMinutes"] ?? "30");
// 1. 定义 Claims(存储用户信息)
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userId), // 用户ID
new Claim(ClaimTypes.Name, userName), // 用户名
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // 唯一标识(防重放)
};
// 添加角色 Claim
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
// 2. 创建密钥
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
// 3. 生成 Token
var token = new JwtSecurityToken(
issuer: issuer,
audience: audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(expiresInMinutes), // 过期时间(UTC时间)
signingCredentials: credentials
);
// 4. 序列化 Token
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
}
配置服务(Program.cs)
using Identity框架;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Data;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<MyDbContext>(ops =>
{
ops.UseSqlServer("server=.;database=Identity;uid=sa;pwd=[XXXXX];TrustServerCertificate=True;");
});
builder.Services.AddDataProtection(); //安全地加密、解密应用程序中的敏感数据
builder.Services.AddIdentityCore<MyUser>(options => //注意不是Addldentity,Addldentity是传统MVC的
{
options.Password.RequireDigit = false; //表示不再强制要求密码中包含数字。
options.Password.RequireNonAlphanumeric = false; //表示不再强制要求密码中包含非字母数字字符(如 !@#$%^&* 等特殊符号)。
options.Password.RequireLowercase = false; //表示不再强制要求密码中包含小写字母。
options.Password.RequireUppercase = false; //表示不再强制要求密码中包含大写字母。
options.Password.RequiredLength = 6; //表示密码的最小长度为6。
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider; //表示密码重置令牌提供程序的默认值。这意味着当用户请求重置密码时,系统会生成一个通过电子邮件发送的令牌,用户通过该令牌完成密码的重置操作。
options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider; //表示电子邮件确认令牌提供程序的默认值。即当用户注册新账户后,系统会生成一个用于确认邮箱的令牌,并通过电子邮件发送给用户,用户点击链接或输入令牌以完成邮箱验证。
});
//IdentityBuilder是ASP.NET Core Identity提供的一个核心类,用于配置用户和角色相关的服务和功能。
//1. typeof(MyUser):指定了应用程序中使用的用户类型,这里是自定义的MyUser类,通常继承自IdentityUser,用于表示系统中的用户。
//2. typeof(MyRole):指定了应用程序中使用的角色类型,这里是自定义的MyRole类,通常继承自IdentityRole,用于表示系统中的角色。
var idBuilder = new IdentityBuilder(typeof(MyUser), typeof(MyRole), builder.Services);
/**
* 这段代码通过IdentityBuilder配置了ASP.NET Core Identity框架,使其能够使用自定义的用户和角色类型(MyUser和MyRole),
* 并将数据存储在Entity Framework Core的MyDbContext中。同时,它还注册了必要的管理器(UserManager和RoleManager)和默认的令牌提供程序,
* 以支持用户和角色的全面管理功能,包括身份验证、授权、密码重置等常见操作。这些配置是构建安全、可扩展的身份验证和授权系统的基础。
*/
idBuilder.AddEntityFrameworkStores<MyDbContext>()
.AddDefaultTokenProviders()
.AddRoleManager<RoleManager<MyRole>>()
.AddUserManager<UserManager<MyUser>>();
// 1. 注册 JWT 认证服务
builder.Services.AddAuthentication(options =>
{
// 设置默认默认认证方案:使用 JWT Bearer
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// 从配置文件读取 JWT 参数
var jwtSettings = builder.Configuration.GetSection("Jwt");
var secretKey = jwtSettings["SecretKey"] ?? throw new ArgumentNullException("Jwt:SecretKey is not configured");
// 配置 Token 验证参数
options.TokenValidationParameters = new TokenValidationParameters
{
// 验证签发者(Issuer)
ValidateIssuer = true,
ValidIssuer = jwtSettings["Issuer"],
// 验证受众(Audience)
ValidateAudience = true,
ValidAudience = jwtSettings["Audience"],
// 验证密钥
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)),
// 验证 Token 过期时间
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero // 禁用默认的 5 分钟时钟偏差
};
});
// 2. 注册授权服务(如需基于角色/策略的授权)
builder.Services.AddAuthorization();
//3. 注册JwtTokenService
builder.Services.AddScoped<JwtTokenService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// 4. 启用认证中间件(必须在授权中间件之前)
app.UseAuthentication();
// 5. 启用授权中间件
app.UseAuthorization();
app.MapControllers();
app.Run();
创建登陆接口(生成Token)
添加登陆控制器 LoginController.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Identity框架.Controllers
{
[ApiController]
[Route("[controller]")]
public class LoginController : ControllerBase
{
private readonly JwtTokenService _tokenService;
public LoginController(JwtTokenService tokenService)
{
_tokenService = tokenService;
}
// 登录接口(示例:简化版,实际需验证用户名密码)
[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
// 1. 实际项目中需验证用户名密码(例如从数据库查询)
// 这里简化处理,假设验证通过
var userId = "123"; // 从数据库获取的用户ID
var userName = "admin"; // 从数据库获取的用户名
var roles = new List<string> { "Admin", "User" }; // 从数据库获取的角色
// 2. 生成 Token
var token = _tokenService.GenerateToken(userId, userName, roles);
// 3. 返回 Token
return Ok(new LoginResponse
{
Token = token,
ExpiresIn = 30 // 有效期(分钟,需与配置一致)
});
}
}
// 登录请求参数
public class LoginRequest
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
// 登录响应(包含 Token)
public class LoginResponse
{
public string Token { get; set; } = string.Empty;
public int ExpiresIn { get; set; }
}
}
Login接口返回
保护 API 接口(验证 Token)
使用 [Authorize]
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
测试
测试1,不使用Header, Authorization
测试2,使用错误的Authorization
测试3,使用返回的token
五、防范使用他人的 JWT Token 进行接口访问
在 .NET Core 8 中,如果使用他人的 JWT Token 进行接口访问,会引发一系列安全问题和业务逻辑异常,具体影响如下:
1. 身份冒用与权限越界
- 核心问题:JWT Token 包含用户的身份标识(如
sub
声明通常对应用户 ID)和权限信息(如role
声明)。使用他人 Token 会导致系统误将攻击者识别为合法用户。 - 具体风险:
- 攻击者可访问受害者的个人数据(如订单、消息)。
- 若受害者具有管理员权限,攻击者可执行管理员操作(如删除数据、修改配置)。
- 示例:用户 A 的 Token 包含
role: "Admin"
,攻击者使用该 Token 可访问/api/admin
等受保护接口。
2. 业务数据混乱
- 系统会基于 Token 中的用户身份(如
UserId
)执行操作,使用他人 Token 会导致:- 操作记录归属错误(如用用户 B 的 Token 下单,订单会被记到 B 名下)。
- 数据权限校验失效(如用户 A 只能查看自己的订单,用 B 的 Token 可查看 B 的订单)。
3. 审计与追责困难
- 系统日志会记录 Token 中的用户身份,而非实际操作人。一旦发生安全事件,无法通过日志定位真实攻击者,增加追责难度。
4. Token 本身的合法性不影响冒用
- 即使 Token 是真实有效的(未过期、签名正确),只要被他人窃取并使用,就会引发上述问题。
- .NET Core 的 JWT 验证机制仅检查 Token 的格式合法性(签名、过期时间等),不验证使用人是否为 Token 签发对象。
如何避免此类问题?
-
缩短 Token 有效期
减少 Token 被窃取后可滥用的时间窗口(如设置 15-30 分钟过期)。 -
使用 HTTPS 传输
防止 Token 在传输过程中被中间人窃取(Authorization
头明文传输)。 -
实现 Token 绑定
将 Token 与客户端特征(如 IP 地址、设备指纹)绑定,在验证时额外检查:// 在 JwtBearer 验证中添加自定义验证 options.Events = new JwtBearerEvents { OnTokenValidated = context => { // 获取客户端 IP var clientIp = context.HttpContext.Connection.RemoteIpAddress?.ToString(); // 从 Token 中获取签发时的 IP(需在生成 Token 时添加到 Claim) var tokenIp = context.Principal.FindFirstValue("client_ip"); if (clientIp != tokenIp) { context.Fail("Token 与当前设备不匹配"); } return Task.CompletedTask; } };
-
维护 Token 黑名单
当用户注销或 Token 泄露时,将 Token 加入黑名单(如用 Redis 存储),验证时检查:OnTokenValidated = context => { var jwtId = context.Principal.FindFirstValue(JwtRegisteredClaimNames.Jti); var redis = context.HttpContext.RequestServices.GetRequiredService<IDatabase>(); if (redis.KeyExists($"blacklist:{jwtId}")) { context.Fail("Token 已被吊销"); } return Task.CompletedTask; };
-
敏感操作二次验证
对于关键操作(如转账、修改密码),即使 Token 有效,仍要求用户输入密码或验证码。
总结
使用他人的 JWT Token 本质是身份冒用,会导致权限越界、数据混乱和安全风险。.NET Core 的 JWT 验证机制本身不解决 “Token 归属” 问题,需通过缩短有效期、加密传输、额外验证等手段降低风险。