asp.net core 8 JWT的运用

一、什么是JWT

image

JWT(JSON Web Token)是一种基于 JSON 的轻量级身份验证和信息交换规范,广泛用于分布式系统中的用户身份验证、信息传递等场景。它通过数字签名保证信息的完整性和真实性,无需在服务端存储会话状态,非常适合前后端分离、微服务架构等场景。

一、JWT 的核心优势

  1. 无状态:服务端无需存储会话信息,减轻服务器负担,便于水平扩展
  2. 自包含:Token 本身包含用户身份、权限等关键信息,减少数据库查询
  3. 跨域支持:可在不同域之间安全传递,适合分布式系统
  4. 结构简单:基于 JSON 格式,易于解析和使用

二、JWT 的结构

JWT 由三部分组成,用.分隔,格式为:Header.Payload.Signature
例如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiSm9obiBEb2UifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

image

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 的工作流程

  1. 用户登录:用户提交用户名 / 密码到认证服务器

  2. 生成 Token:认证服务器验证通过后,生成 JWT 并返回给客户端

  3. 客户端存储:客户端(如浏览器)将 Token 存储在 localStorage、sessionStorage 或 Cookie 中

  4. 请求资源:客户端每次请求需在 HTTP 头部携带 Token,格式:

    Authorization: Bearer <token>
    
  5. 服务器验证:资源服务器解析 Token,验证签名和过期时间,确认有效后返回资源

四、JWT 的优缺点

优点

  • 无状态:服务端无需存储会话,适合分布式系统
  • 跨域支持:可在不同服务间传递,简化单点登录(SSO)实现
  • 自包含:减少数据库查询,提升性能

缺点

  • 无法撤销:Token 一旦签发,在有效期内始终有效(除非服务器维护黑名单)
  • payload 不加密:敏感信息不能放在 payload 中
  • 过期时间固定:无法动态修改有效期(需重新签发)

五、JWT 的安全最佳实践

  1. 使用 HTTPS:防止 Token 在传输过程中被窃取
  2. 设置合理过期时间:短期有效(如 15-30 分钟),降低被盗用风险
  3. 敏感信息不存 payload:payload 仅 Base64 编码,无加密
  4. 使用非对称加密:公钥验签,私钥签名,避免密钥泄露风险
  5. 实现 Token 刷新机制:过期前用刷新令牌(Refresh Token)获取新 Token
  6. 维护黑名单:对已注销但未过期的 Token 进行拦截

六、JWT 的应用场景

  • 前后端分离项目的身份验证
  • 微服务架构中的服务间认证
  • 单点登录(SSO)系统
  • API 接口授权
  • 安全的信息传递(如服务间数据交换)

JWT 通过简洁的结构和无状态特性,成为现代 Web 应用中身份验证的重要方案,但需结合安全最佳实践使用,平衡便捷性和安全性。

二、asp.net core 8 中的 JWT组件

System.IdentityModel.Tokens.JwtMicrosoft.AspNetCore.Authentication.JwtBearerASP.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 的合法性(底层算法实现)。
  • 核心类JwtSecurityTokenJwtSecurityTokenHandlerJwtRegisteredClaimNames 等。

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);

image

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接口返回

image

保护 API 接口(验证 Token)

使用 [Authorize]

[ApiController]
    [Route("[controller]")]
    [Authorize]
    public class WeatherForecastController : ControllerBase

测试

测试1,不使用Header, Authorization

image

测试2,使用错误的Authorization

image

测试3,使用返回的token

image

五、防范使用他人的 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 签发对象

如何避免此类问题?

  1. 缩短 Token 有效期
    减少 Token 被窃取后可滥用的时间窗口(如设置 15-30 分钟过期)。

  2. 使用 HTTPS 传输
    防止 Token 在传输过程中被中间人窃取(Authorization 头明文传输)。

  3. 实现 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;
        }
    };
    
  4. 维护 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;
    };
    
  5. 敏感操作二次验证
    对于关键操作(如转账、修改密码),即使 Token 有效,仍要求用户输入密码或验证码。

总结

使用他人的 JWT Token 本质是身份冒用,会导致权限越界、数据混乱和安全风险。.NET Core 的 JWT 验证机制本身不解决 “Token 归属” 问题,需通过缩短有效期、加密传输、额外验证等手段降低风险。

posted @ 2025-08-30 21:33  【唐】三三  阅读(4)  评论(0)    收藏  举报