RuoVea.ExJwtBearer 10.0.0.6

dotnet add package RuoVea.ExJwtBearer --version 10.0.0.6
                    
NuGet\Install-Package RuoVea.ExJwtBearer -Version 10.0.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RuoVea.ExJwtBearer" Version="10.0.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.ExJwtBearer" Version="10.0.0.6" />
                    
Directory.Packages.props
<PackageReference Include="RuoVea.ExJwtBearer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RuoVea.ExJwtBearer --version 10.0.0.6
                    
#r "nuget: RuoVea.ExJwtBearer, 10.0.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RuoVea.ExJwtBearer@10.0.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RuoVea.ExJwtBearer&version=10.0.0.6
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.ExJwtBearer&version=10.0.0.6
                    
Install as a Cake Tool

🔐 RuoVea.ExJwtBearer

一站式 .NET 认证授权工具库 — 集成 JWT、Basic、IdentityServer4、Authing 四种认证模式,提供 Token 生命周期管理、刷新令牌、权限鉴权、RESTful 结果格式化、SignalR 支持,并内置中、英、法、日、粤语、越南语等 7 种语言的异常提示。


📖 目录


📋 概览

RuoVea.ExJwtBearer 为 .NET 开发者提供了开箱即用的认证与授权基础设施封装,避免手动配置 AddJwtBearer / AddAuthorization / TokenValidationParameters 的繁琐细节。

 ┌──────────────────────────────────────────────────────────────────┐
 │                      RuoVea.ExJwtBearer                          │
 ├──────────────────────────────────────────────────────────────────┤
 │  认证模式                    鉴权 & 工具                          │
 │  ├─ JWT (标准/自定义Handler)  ├─ 权限策略鉴权 (Policy)            │
 │  ├─ Basic (基础认证)          ├─ 角色鉴权 (Admin/System/Client)   │
 │  ├─ IdentityServer4 Client    ├─ IJwtHelper (Token 加解密)        │
 │  └─ Authing                   ├─ 刷新Token 自动续期               │
 │                               ├─ RESTful 401/403 格式化           │
 │  用户上下文                    ├─ BearerAuthorize 快捷特性         │
 │  └─ AspNetUser : ICurrentUser └─ SignalR query string 支持        │
 ├──────────────────────────────────────────────────────────────────┤
 │  国际化: zh-CN | zh-TW | zh-HK | en-US | fr-FR | ja-JP | vi-VN   │
 └──────────────────────────────────────────────────────────────────┘

设计原则

原则 说明
一键注册 AddAuthenticationSetup(identify) 按需选择认证模式,自动挂载对应 Handler
安全默认值 HMAC-SHA256 签名、ClockSkew=5s 容错、Token 过期默认 20 分钟回退
刷新令牌防重放 依赖 IDistributedCache 登记已使用的 RefreshToken,黑名单机制杜绝重放
零入侵 RESTful 401/403 自动返回 JSON 格式(IRESTfulResult),无需手动处理
全局鉴权开关 globalAuthorize=true 一行启用全局 [Authorize],无需逐 Controller 标记

⚠️ IdentifyEnum 枚举值注意: 必须使用 IdentifyEnum.Jwt / IdentifyEnum.Basic / IdentifyEnum.Ids4 / IdentifyEnum.Authing,不要误写成 JwtBearer / BasicAuth / IdentityServer4 / Authing。


📦 安装

.NET CLI

# .NET 8.0
dotnet add package RuoVea.ExJwtBearer --version 8.0.0.10

# .NET 10.0
dotnet add package RuoVea.ExJwtBearer --version 10.0.0.4

Package Manager

Install-Package RuoVea.ExJwtBearer -Version 8.0.0.10

PackageReference

<PackageReference Include="RuoVea.ExJwtBearer" Version="8.0.0.10" />

支持的 Target Framework

TFM 最低版本
net8.0 8.0.0.10
net10.0 10.0.0.4

传递依赖

依赖包 net8.0 版本 net10.0 版本
Microsoft.AspNetCore.Authentication.JwtBearer 8.0.27 10.0.8
Newtonsoft.Json 13.0.4 13.0.4
NetDevPack.Security.JwtExtensions 8.0.0 9.0.1
RuoVea.ExConfig 8.0.* 10.0.*
RuoVea.ExDto 8.0.* 10.0.*

⚡ 30 秒快速开始

1. 导入命名空间

using RuoVea.ExJwtBearer;              // IdentifyEnum, IJwtHelper, UserVo, BearerAuthorizeAttribute
using RuoVea.ExJwtBearer.Configs;      // JwtConfig, BasicAuthConfig, Ids4Client, BasicUser
using RuoVea.ExJwtBearer.Extensions;   // 扩展方法(通常 global using 已覆盖)

2. 一行代码注册认证

// <inheritdoc cref="AuthenticationSetup.AddAuthenticationSetup"/>
// Program.cs / Startup.cs
builder.Services.AddAuthenticationSetup(IdentifyEnum.Jwt);

3. 配置 appsettings.json

{
  "Jwt": {
    "ValidateIssuerSigningKey": true,
    "IssuerSigningKey": "3c1cbc3f546eda35168c3aa3cb91780fbe703f0996c6d123ea96dc85c70bbc0a",
    "ValidateIssuer": true,
    "ValidIssuer": "MyApp",
    "ValidateAudience": true,
    "ValidAudience": "MyApp.Audience",
    "ValidateLifetime": true,
    "ExpiredTime": 1440,
    "ClockSkew": 5
  }
}

4. 生成第一个 Token

// <inheritdoc cref="IJwtHelper.CreateToken"/>
public class AuthController : ControllerBase
{
    private readonly IJwtHelper _jwtHelper;

    public AuthController(IJwtHelper jwtHelper) => _jwtHelper = jwtHelper;

    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginRequest req)
    {
        var user = new UserVo { Id = 1, Account = req.Username, Name = req.Username, AdminType = "1" };
        var token = _jwtHelper.CreateToken(user);
        return Ok(new { accessToken = token });
    }
}

30 秒内你完成了: NuGet 安装 → 一行注册 JWT 认证 → 配置密钥与过期时间 → 注入 IJwtHelper 生成第一个 Token。


🧩 核心场景

场景一:JWT 认证配置

┌──────────────┐   AddAuthenticationSetup(IdentifyEnum.Jwt)   ┌──────────────────────┐
│  IServiceCol- │ ──────────────────────────────────────────▶ │  JwtBearerDefaults   │
│  lection      │                                             │  + ApIResponseHandler│
└──────────────┘                                             │  + JwtHelper         │
                                                              └──────────────────────┘
🔹 标准 JWT(从 appsettings.json 读取 "Jwt" 节点)
// <inheritdoc cref="Authentication_JwtSetup.AddAuthenticationJwtSetup(IServiceCollection, bool)"/>
// 自动读取 AppSettings.GetSection("Jwt")
services.AddAuthenticationSetup(IdentifyEnum.Jwt);                         // 全局鉴权=关
services.AddAuthenticationSetup(IdentifyEnum.Jwt, globalAuthorize: true);   // 全局鉴权=开
🔹 自定义配置节点
// <inheritdoc cref="Authentication_JwtSetup.AddAuthenticationJwtSetup(IServiceCollection, IConfiguration, bool)"/>
var config = Configuration.GetSection("CustomJwt");
services.AddAuthenticationJwtSetup(config, globalAuthorize: true);
🔹 自定义 Handler(替换默认 ApIResponseHandler)
// <inheritdoc cref="Authentication_JwtSetup.AddAuthenticationJwtSetup{AuthenticationHandler}(IServiceCollection, bool)"/>
services.AddAuthenticationJwtSetup<MyCustomHandler>(globalAuthorize: false);

// 带自定义配置节点
services.AddAuthenticationJwtSetup<MyCustomHandler>(config, globalAuthorize: true);
🔹 使用泛型入口自动分发
// <inheritdoc cref="AuthenticationSetup.AddAuthenticationSetup{T}(IServiceCollection, IdentifyEnum, bool)"/>
services.AddAuthenticationSetup<MyCustomHandler>(IdentifyEnum.Jwt, globalAuthorize: true);

⚠️ 注意: 自定义 Handler 必须继承 AuthenticationHandler<AuthenticationSchemeOptions>。

appsettings.json 配置详解
{
  /* JWT 认证配置 */
  "Jwt": {
    "ValidateIssuerSigningKey": true,   // 是否验证签发方密钥,bool? 类型
    "IssuerSigningKey": "your-256bit-secret-key-min-16-chars!!", // 密钥,string,长度须>=16
    "ValidateIssuer": true,             // 是否验证签发方,bool? 类型
    "ValidIssuer": "MyApplication",     // 签发方标识,string 类型
    "ValidateAudience": true,           // 是否验证签收方,bool? 类型
    "ValidAudience": "MyApp.Audience",  // 签收方标识,string 类型
    "ValidateLifetime": true,           // 是否验证过期时间,bool? 类型,建议 true
    "ClockSkew": 5,                     // 时钟偏差容错值,long? 类型,单位:秒
    "ExpiredTime": 1440,                // Token 过期时间,long? 类型,单位:分钟
    "Algorithm": "HmacSha256"           // 签名算法,string,默认 HmacSha256
  }
}
管道注册(Program.cs)
// ⚠️ 必须按此顺序:先 UseAuthentication 再 UseAuthorization
app.UseAuthentication();
app.UseAuthorization();

❗ 401 问题排查: 绝大部分 401 错误是因为未按顺序注册 UseAuthentication() 和 UseAuthorization(),或 IssuerSigningKey 配置不一致。


场景二:Basic 认证配置

┌──────────────┐   AddAuthenticationBasicSetup()   ┌───────────────────────────┐
│  IServiceCol- │ ───────────────────────────────▶ │  BasicAuthenticationHandler│
│  lection      │                                   │  + 用户名/密码验证         │
└──────────────┘                                   │  + 角色 Claim 注入          │
                                                    └───────────────────────────┘
🔹 标准 Basic(从 appsettings.json 读取 "BasicAuth" 节点)
// <inheritdoc cref="Authentication_BasicSetup.AddAuthenticationBasicSetup(IServiceCollection, bool)"/>
services.AddAuthenticationSetup(IdentifyEnum.Basic);
🔹 从 IConfiguration 节点
// <inheritdoc cref="Authentication_BasicSetup.AddAuthenticationBasicSetup(IServiceCollection, IConfiguration, bool)"/>
services.AddAuthenticationBasicSetup(Configuration.GetSection("MyBasicAuth"));
🔹 通过 Action 直接配置
// <inheritdoc cref="Authentication_BasicSetup.AddAuthenticationBasicSetup(IServiceCollection, Action{BasicAuthConfig}, bool)"/>
services.AddAuthenticationBasicSetup(options =>
{
    options.ShowFrom = true;
    options.users = new List<BasicUser>
    {
        new BasicUser { username = "admin", password = "admin@123", role = "Admin" },
        new BasicUser { username = "guest", password = "guest", role = "Client" }
    };
});
🔹 自定义 Handler + Action 配置(共 6 个重载)
services.AddAuthenticationBasicSetup<MyBasicHandler>(opt =>
{
    opt.ShowFrom = false;
    opt.users = new List<BasicUser> { new() { username = "api", password = "key", role = "System" } };
}, globalAuthorize: true);
appsettings.json 配置
{
  /* Basic 认证配置 */
  "BasicAuth": {
    "ShowFrom": true,       // 是否触发浏览器弹窗(WWW-Authenticate 头),bool 类型
    "users": [
      { "username": "admin", "password": "admin@123", "role": "Admin" },
      { "username": "api", "password": "sk-api-key", "role": "System" }
    ]
  }
}

⚠️ 安全警告: Basic 认证将凭证以 Base64 形式传输,仅适合内网或开发环境。生产环境请务必配合 HTTPS 使用。


场景三:IdentityServer4 认证配置

┌──────────────┐   AddAuthentication_Ids4Setup()   ┌────────────────────────┐
│  IServiceCol- │ ────────────────────────────────▶ │  JwtBearer + Authority │
│  lection      │                                    │  + Audience 验证       │
└──────────────┘                                    └────────────────────────┘
🔹 标准 IDS4
// <inheritdoc cref="Authentication_Ids4Setup.AddAuthentication_Ids4Setup(IServiceCollection, bool)"/>
services.AddAuthenticationSetup(IdentifyEnum.Ids4);
🔹 自定义配置节点
services.AddAuthentication_Ids4Setup(Configuration.GetSection("Ids4Client"));
🔹 带自定义 Handler
services.AddAuthentication_Ids4Setup<MyIds4Handler>(Configuration.GetSection("Ids4Client"));
appsettings.json 配置
{
  /* IdentityServer4 Client 认证配置 */
  "Ids4Client": {
    "AuthorizationUrl": "https://auth.myapp.com",   // 认证中心域名,string 类型
    "ApiName": "my.api.resource"                     // 资源服务器名称,string 类型
  }
}

❗ 注意: IDS4 模式下 RequireHttpsMetadata 默认设为 false,生产环境应改为 true 或通过反向代理配置 HTTPS 终止。


场景四:Authing 认证配置

┌──────────────┐   AddAuthentication_AuthingSetup()  ┌────────────────────────┐
│  IServiceCol- │ ──────────────────────────────────▶ │  JwtBearer + JwksUri   │
│  lection      │                                      │  + ApIResponseHandler  │
└──────────────┘                                      └────────────────────────┘

Authing 模式复用了 JwtConfig 配置节点,但内部通过 AddJwtBearer + SetJwksOptions 实现 Authing 专属的 JWKS 端点验证(注释代码中保留了集成入口)。

// <inheritdoc cref="Authentication_AuthingSetup.AddAuthentication_AuthingSetup(IServiceCollection, bool)"/>
services.AddAuthenticationSetup(IdentifyEnum.Authing);

// 带自定义 Handler
services.AddAuthentication_AuthingSetup<MyAuthingHandler>();

⚠️ 注意: Authing 模式需要调用方自行配置 JwksOptions(JwksUri、Issuer),目前源码中以注释形式保留,请按实际 Authing 应用配置启用以实现完整的 JWKS 动态密钥轮换。


场景五:权限鉴权配置

┌──────────────────────┐
│  ApplicationPermission│  ← 实现 VerifyPermissionAsync 方法
│  (abstract class)     │
├──────────────────────┤
│  PolicyRequirement    │  ← IAuthorizationRequirement
│  Permissions.Name     │  ← "Permission" 授权策略名
│  BearerAuthorize      │  ← [Authorize("Permission")] 快捷特性
└──────────────────────┘
🔹 创建自定义权限处理器
// <inheritdoc cref="ApplicationPermission.VerifyPermissionAsync"/>
public class MyPermission : ApplicationPermission
{
    private readonly IUserService _userService;

    public MyPermission(IRESTfulResult restfulResult, IUserService userService)
        : base(restfulResult) => _userService = userService;

    /// <summary>
    /// 验证用户对当前路由的访问权限
    /// </summary>
    /// <param name="httpContext">HTTP 上下文</param>
    /// <param name="permission">路由名称,格式 "Area:Controller:Action"(冒号分隔)</param>
    public override async Task<bool> VerifyPermissionAsync(DefaultHttpContext httpContext, string permission)
    {
        var userId = httpContext.User.FindFirst(ClaimConst.CLAINM_USERID)?.Value;
        if (string.IsNullOrWhiteSpace(userId)) return false;

        // 调用业务层权限校验
        return await _userService.HasPermissionAsync(long.Parse(userId), permission);
    }

    // 可选:重写 VerifyRequirement 做前置校验(优先级高于 VerifyPermissionAsync)
    public override void VerifyRequirement(AuthorizationHandlerContext context, PolicyRequirement requirement)
    {
        // 例如:检查请求头中是否包含特定标识
    }
}
🔹 注册权限鉴权
// <inheritdoc cref="AuthorizationSetup.AddAuthorizationSetup{T}(IServiceCollection, bool)"/>
// 单泛型:默认以 AspNetUser 作为用户上下文
services.AddAuthorizationSetup<MyPermission>();

// <inheritdoc cref="AuthorizationSetup.AddAuthorizationSetup{T, TUser}(IServiceCollection, bool)"/>
// 双泛型:指定自定义用户上下文类型
services.AddAuthorizationSetup<MyPermission, CustomUser>(globalAuthorize: true);

⚠️ 双泛型参数重要提示: AddAuthorizationSetup<T, TUser> 需要两个类型参数 —— 第一个是权限处理器 T : ApplicationPermission,第二个是用户上下文 TUser : ICurrentUser。单泛型版本自动使用 AspNetUser 作为 TUser 默认值。

🔹 内置角色策略

注册时自动创建以下角色策略(无需额外配置):

策略名称 要求
Client RequireRole("Client")
Admin RequireRole("Admin")
SystemOrAdmin RequireRole("Admin", "System")
A_S_O RequireRole("Admin", "System", "Others")
// 使用内置角色策略
[Authorize(Policy = "Admin")]
[HttpGet("admin-only")]
public IActionResult AdminOnly() => Ok();

// 使用权限策略(需配合 ApplicationPermission 实现类)
[Authorize(Permissions.Name)]  // 等价于 [Authorize("Permission")]
[HttpGet("protected")]
public IActionResult Protected() => Ok();
🔹 BearerAuthorize 快捷特性
// <inheritdoc cref="BearerAuthorizeAttribute"/>
// [BearerAuthorize] 等价于 [Authorize(Permissions.Name)] 的语义简化
[BearerAuthorize]
[HttpGet("data")]
public IActionResult GetData() => Ok();

场景六:创建与验证 Token

┌────────────┐    IJwtHelper.CreateToken(user)     ┌──────────────┐
│  UserVo     │ ─────────────────────────────────▶ │  JWT Token    │
│ + claims    │                                     │  (string)     │
└────────────┘                                     └──────┬───────┘
                                                          │
                                                IJwtHelper.ValidateAsync(token)
                                                          │
                                                          ▼
                                                  ┌──────────────┐
                                                  │  (IsValid,   │
                                                  │   Token,     │
                                                  │   Result)    │
                                                  └──────────────┘
🔹 通过 UserVo 创建 Token(推荐)
// <inheritdoc cref="IJwtHelper.CreateToken"/>
var user = new UserVo
{
    Id = 123,
    TenantId = 1,
    Account = "zhangsan",
    Name = "张三",
    AdminType = "1"   // AdminType: 1=SuperAdmin, 2=Admin, 其他=普通用户
};

// 基础用法 — 20 分钟默认过期(或 appsettings 中的 ExpiredTime)
string token = _jwtHelper.CreateToken(user);

// 带扩展 Claim — 扩展数据优先于 UserVo 字段
var claims = new List<Claim>
{
    new Claim("Department", "Engineering"),
    new Claim("Level", "P7")
};
string tokenWithClaims = _jwtHelper.CreateToken(user, claims, expiredTime: 60);
🔹 通过 Dictionary 构建 Token
// <inheritdoc cref="IJwtHelper.TokenBuilder"/>
var extends = new Dictionary<string, object>
{
    { "CustomKey", "CustomValue" },
    { "Roles", new[] { "Admin", "User" } }  // 支持数组
};
string token = _jwtHelper.TokenBuilder(user, extends, expiredTime: 30);
🔹 原始 Encrypt(最低层 API)
// <inheritdoc cref="IJwtHelper.Encrypt(IDictionary{string, object}, long?)"/>
var payload = new Dictionary<string, object>
{
    { ClaimConst.CLAINM_USERID, 123 },
    { ClaimConst.TENANT_ID, 1 },
    { ClaimConst.CLAINM_ACCOUNT, "admin" },
    { ClaimConst.CLAINM_NAME, "管理员" },
    { ClaimConst.CLAINM_SUPERADMIN, "1" }
};
string token = _jwtHelper.Encrypt(payload, expiredTime: 120);

// 自定义密钥和算法
string customToken = _jwtHelper.Encrypt(
    issuerSigningKey: "my-custom-256-bit-key-here!!!",
    payload: payload,
    algorithm: SecurityAlgorithms.HmacSha512
);
🔹 验证 Token
// <inheritdoc cref="IJwtHelper.ValidateAsync"/>
// NET6+ 异步版本
var (isValid, jsonWebToken, validationResult) = await _jwtHelper.ValidateAsync(accessToken);
if (isValid)
{
    var userId = jsonWebToken.GetPayloadValue<string>(ClaimConst.CLAINM_USERID);
    // 业务逻辑...
}

// pre-NET6 同步版本(方法名无 Async 后缀,但仍返回 Task/Tuple)
var (isValid, jsonWebToken) = _jwtHelper.ValidateJwtBearerToken(httpContext);
🔹 读取 Token(不校验签名/过期)
// <inheritdoc cref="IJwtHelper.ReadJwtToken"/>
JsonWebToken jwt = _jwtHelper.ReadJwtToken(accessToken);
var account = jwt.GetPayloadValue<string>(ClaimConst.CLAINM_ACCOUNT);

场景七:刷新 Token 自动续期

┌──────────────┐                          ┌──────────────────┐
│  过期 Access  │  ① Authorization Header  │  AutoRefreshToken │
│  Token       │ ──────────────────────▶ │  Async()          │
│              │                          │                   │
│  刷新 Token   │  ② X-Authorization 头    │  ③ 验证刷新Token  │
│              │ ──────────────────────▶ │  ④ 重新签发       │
└──────────────┘                          │  ⑤ 响应头返回     │
                                           │     access-token  │
                                           │     x-access-token│
                                           └──────────────────┘
🔹 生成刷新令牌
// <inheritdoc cref="IJwtHelper.GenerateRefreshToken"/>
string refreshToken = _jwtHelper.GenerateRefreshToken(accessToken, expiredTime: 43200);
// 43200 分钟 = 30 天(默认值)
🔹 在权限处理器中调用自动续期
// <inheritdoc cref="IJwtHelper.AutoRefreshTokenAsync"/>
public class RefreshPermission : ApplicationPermission
{
    private readonly IJwtHelper _jwtHelper;

    public RefreshPermission(IRESTfulResult restfulResult, IJwtHelper jwtHelper)
        : base(restfulResult) => _jwtHelper = jwtHelper;

    public override async Task<bool> VerifyPermissionAsync(DefaultHttpContext httpContext, string permission)
    {
        // 触发自动刷新(内部自动判断是否已过期)
        await _jwtHelper.AutoRefreshTokenAsync(context: null, httpContext: httpContext);
        // ...
        return true;
    }
}
🔹 手动交换过期 Token
// <inheritdoc cref="IJwtHelper.ExchangeAsync"/>
string newToken = await _jwtHelper.ExchangeAsync(
    expiredToken: oldAccessToken,
    refreshToken: refreshToken,
    expiredTime: 60,      // 新 Token 60 分钟过期
    clockSkew: 5           // 容错 5 秒(并发场景)
);
🔹 自动刷新机制详解
步骤 说明
① 判断 Identity 若 context.User.Identity.IsAuthenticated == true 则跳过
② 匿名检查 若 Endpoint 有 [AllowAnonymous] 则跳过
③ 获取 Token 从 Authorization 头获取过期 Token,从 X-Authorization 头获取刷新 Token
④ 校验刷新Token 验证签名与过期时间,检查黑名单
⑤ 签名匹配 校验刷新 Token 中的 f/e/k 字段与原始 Token 的 header/payload/signature 片段匹配
⑥ 签发新 Token 提取原 Token Claims(剔除 StationaryClaimTypes:iat, nbf, exp, iss, aud),重新签名
⑦ 黑名单登记 将刷新 Token 写入 IDistributedCache,Key=BLACKLIST_REFRESH_TOKEN:{token}
⑧ 响应头返回 access-token 返回新 Token,x-access-token 返回新刷新 Token
⑨ CORS 处理 自动追加 Access-Control-Expose-Headers: access-token, x-access-token

⚠️ IDistributedCache 必须注册: 刷新 Token 的黑名单机制依赖 IDistributedCache(如 AddStackExchangeRedisCache 或 AddDistributedMemoryCache),否则刷新检测将静默失败,导致同一 RefreshToken 可被重复使用。

❗ StationaryClaimTypes: 交换 Token 时会自动剔除 iat, nbf, exp, iss, aud 这 5 个声明,防止新旧 Token 的固定声明冲突。


场景八:RESTful 结果与 401/403 处理

┌──────────────┐    认证失败    ┌──────────────────────┐
│  HTTP 请求    │ ────────────▶ │  ApIResponseHandler   │
│              │                │  ┌──────────────────┐ │
│  未授权       │                │  │ HandleChallenge   │ │ → 401 JSON
│              │                │  │ HandleForbidden   │ │ → 403 JSON
└──────────────┘                │  └──────────────────┘ │
                                 └──────────────────────┘
🔹 注册 RESTful 结果格式化
// <inheritdoc cref="AuthenticationSetup.DIRESTfulResult"/>
// 通常由 AddAuthenticationSetup 内部自动调用,手动注册:
services.DIRESTfulResult();
🔹 自定义 RESTful 结果格式
// <inheritdoc cref="RESTfulResultExtend.RESTfulResult"/>
// 继承 IRESTfulResult 并重写 RESTfulResult 方法
public class CustomRESTfulResult : IRESTfulResult
{
    public dynamic RESTfulResult(CodeStatus statusCode, object data = default,
        object message = default, object extras = default)
    {
        return new
        {
            code = (int)statusCode,
            msg = message?.ToString() ?? statusCode.ToString(),
            data = data,
            timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
        };
    }
}

// 在 AddAuthenticationSetup 之前注册
services.AddSingleton<IRESTfulResult, CustomRESTfulResult>();
🔹 401/403 响应格式(默认)
// 401 Unauthorized
{
  "Code": 401,
  "Data": null,
  "Message": null,
  "Extras": null,
  "Timestamp": 638756489120000000
}

// 403 Forbidden
{
  "Code": 403,
  "Data": null,
  "Message": null,
  "Extras": null,
  "Timestamp": 638756489130000000
}
🔹 Handler 说明
Handler 类 作用
ApIResponseHandler JWT/Authing 认证失败时,返回 JSON 格式的 401/403 响应(替代默认的质询重定向)
BasicAuthenticationHandler Basic 认证处理:解析 Authorization 头 → Base64 解码 → 比对用户名/密码 → 签发 ClaimsPrincipal;也自带 JSON 401/403

场景九:SignalR 集线器支持

┌──────────────┐   ?access_token=xxx   ┌──────────────────────┐
│  SignalR      │ ────────────────────▶ │  JwtBearerEvents      │
│  Client       │   /api2/chathub       │  OnMessageReceived    │
└──────────────┘                        │  context.Token = xxx  │
                                         └──────────────────────┘

JWT 认证内置了 SignalR 的 Token 传递支持,无需额外配置:

// 前端 SignalR 连接示例
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/api2/chathub", {
        accessTokenFactory: () => loginToken
    })
    .build();

SignalR JS 客户端会自动将 accessTokenFactory 返回的 Token 附加到 query string 参数 access_token 中。服务端 OnMessageReceived 事件检测到路径以 /api2/chathub 开头时,会从 query string 提取 Token 并赋值给 context.Token。


场景十:用户上下文获取

// <inheritdoc cref="AspNetUser"/>
public class MyService
{
    private readonly ICurrentUser _currentUser; // 或直接注入 AspNetUser

    public MyService(ICurrentUser currentUser) => _currentUser = currentUser;

    public object GetUserInfo() => new
    {
        _currentUser.UserId,        // long — 用户 ID
        _currentUser.TenantId,      // long — 租户编码
        _currentUser.Account,       // string — 账号名
        _currentUser.Name,          // string — 昵称
        _currentUser.IsSuperAdmin,  // bool — 是否超级管理员
        _currentUser.IsTenantAdmin, // bool — 是否租户管理员
    };
}

// 注册自定义用户上下文(替代默认 AspNetUser)
services.AddHttpContextSetup<MyCustomUser>(); // where T : ICurrentUser

❗ AddHttpContextSetup 无参版本已废弃: AddHttpContextSetup()(无泛型)标记了 [Obsolete(true)] 会编译错误,必须使用泛型版本 AddHttpContextSetup<T>() where T : ICurrentUser。


⚙️ 配置选项详解

JwtConfig 配置

参数 类型 约束 说明
ValidateIssuerSigningKey bool? — 是否验证签发方密钥
IssuerSigningKey string 长度 >= 16 256-bit 以上签发密钥
ValidateIssuer bool? — 是否验证签发方
ValidIssuer string — 签发方标识
ValidateAudience bool? — 是否验证签收方
ValidAudience string — 签收方标识
ValidateLifetime bool? — 是否验证生存期,建议 true
ClockSkew long? 单位:秒 服务器时间偏差容错值
ExpiredTime long? 单位:分钟 Token 过期时间(回退值:20 分钟)
Algorithm string — 签名算法,默认 HmacSha256

BasicAuthConfig 配置

参数 类型 说明
ShowFrom bool 是否返回 WWW-Authenticate 头触发浏览器弹窗
users List<BasicUser> 用户列表

BasicUser 配置

参数 类型 说明
username string 用户名
password string 密码(明文存储,仅适用于内网)
role string 角色(注入为 ClaimTypes.Role)

Ids4Client 配置

参数 类型 说明
AuthorizationUrl string 认证中心域名(Authority)
ApiName string 资源服务器名称(Audience)

📐 架构与组件速查

认证模式枚举

枚举值 认证类型 说明
IdentifyEnum.Jwt JWT 标准 JWT Bearer 认证(默认)
IdentifyEnum.Basic Basic HTTP Basic Authentication
IdentifyEnum.Ids4 IdentityServer4 IDS4 Client 认证
IdentifyEnum.Authing Authing Authing.cn 认证

扩展方法速查表

方法 重载数 用途
AddAuthenticationSetup 2 主入口,按 IdentifyEnum 选择认证模式
AddAuthenticationSetup<T> 2 主入口 + 自定义 Handler
AddAuthenticationJwtSetup 4 JWT 认证注册
AddAuthenticationBasicSetup 6 Basic 认证注册
AddAuthentication_Ids4Setup 4 IDS4 认证注册
AddAuthentication_AuthingSetup 4 Authing 认证注册
AddAuthorizationSetup<T> 1 权限鉴权注册(TUser=AspNetUser)
AddAuthorizationSetup<T, TUser> 1 权限鉴权注册(自定义 TUser)
AddJwtSetup 3 JWT Helper 独立注册(含 IConfiguration/Action 重载)
AddHttpContextSetup<T> 1 用户上下文注册
DIRESTfulResult 1 RESTful 结果格式化注册

IJwtHelper 接口方法速查表

方法 输入 输出 用途
CreateToken UserVo + List<Claim> string 创建登录 Token
TokenBuilder UserVo + Dictionary<string,object> string 创建 Token(字典模式)
Encrypt(IDictionary, long?) payload + 过期分钟 string 使用默认密钥加密
Encrypt(key, IDictionary, algorithm) 自定义密钥 string 指定密钥/算法加密
Encrypt(key, string, algorithm) JSON string string 字符串 payload 加密
GenerateRefreshToken accessToken + 过期分钟 string 生成刷新 Token
ReadJwtToken accessToken JsonWebToken 读取 Token(不校验)
GetJwtBearerToken HttpContext string 从 Header 提取 Token
CreateTokenValidationParameters JwtConfig TokenValidationParameters 构建验证参数
AutoRefreshTokenAsync context + httpContext Task<bool> NET6+ 自动刷新令牌
ValidateAsync accessToken Task<(bool, JsonWebToken, TokenValidationResult)> NET6+ 异步验证
ExchangeAsync expiredToken + refreshToken Task<string> NET6+ 异步交换令牌
ValidateJwtBearerTokenAsync httpContext Task<(bool, JsonWebToken)> NET6+ 从上下文验证
AutoRefreshToken context + httpContext bool pre-NET6 自动刷新
Validate accessToken (bool, JsonWebToken, TokenValidationResult) pre-NET6 同步验证
Exchange expiredToken + refreshToken string pre-NET6 同步交换
ValidateJwtBearerToken httpContext (bool, JsonWebToken) pre-NET6 从上下文验证

数据模型速查表

类/接口 关键属性 用途
UserVo Id, TenantId, Account, Name, AdminType 登录时传入的用户信息
AspNetUser : ICurrentUser UserId, TenantId, Account, Name, IsSuperAdmin, IsTenantAdmin 当前登录用户上下文
JwtConfig (见上表) JWT 配置映射
BasicAuthConfig ShowFrom, users Basic 认证配置
BasicUser username, password, role Basic 用户
Ids4Client AuthorizationUrl, ApiName IDS4 客户端配置

Handler / 策略类速查表

类 基类/接口 用途
ApIResponseHandler AuthenticationHandler<AuthenticationSchemeOptions> 返回 JSON 格式的 401/403
BasicAuthenticationHandler AuthenticationHandler<AuthenticationSchemeOptions> Basic 认证处理
ApplicationPermission (abstract) AuthorizationHandler<PolicyRequirement> 自定义权限处理器基类
PolicyRequirement IAuthorizationRequirement 权限策略需求
Permissions — 常量类,Name = "Permission"
BearerAuthorizeAttribute AuthorizeAttribute [Authorize("Permission")] 快捷写法
RESTfulResultExtend IRESTfulResult 默认 RESTful 结果格式化
MultiClaimsDictionaryComparer IEqualityComparer<string> Claims 重复键去重(Equals 返回 x!=y,永不等)

用户 Claim 类型常量(来自 RuoVea.ExDto 的 ClaimConst)

常量 含义
ClaimConst.CLAINM_USERID 用户 ID
ClaimConst.TENANT_ID 租户编码
ClaimConst.CLAINM_ACCOUNT 账号名
ClaimConst.CLAINM_NAME 昵称
ClaimConst.CLAINM_SUPERADMIN 管理员类型("1"=超管, "2"=租户管理员)

🌍 国际化

错误消息支持以下语言,根据 CultureInfo.CurrentUICulture 自动切换:

语言 区域代码
简体中文 zh-CN
繁体中文(台湾) zh-TW
粤语(香港) zh-HK
英语 en-US
法语 fr-FR
日语 ja-JP
越南语 vi-VN
// Basic 认证失败时返回国际化消息:
// zh-CN → "认证失败!"
// en-US → "Authentication failed!"
// fr-FR → (法语翻译)
return AuthenticateResult.Fail(i18n.authenticationfailed);

🗺️ 版本迁移指南

从手动 JWT 配置迁移

旧代码模式 迁移到 RuoVea.ExJwtBearer
手动 AddJwtBearer + TokenValidationParameters 逐个配置 services.AddAuthenticationSetup(IdentifyEnum.Jwt) + appsettings.json
手动创建 JwtSecurityTokenHandler + CreateToken _jwtHelper.CreateToken(user)
手动 AuthorizeFilter 全局注册 services.AddAuthenticationSetup(IdentifyEnum.Jwt, globalAuthorize: true)
手动 OnChallenge 写 JSON 401 ApIResponseHandler 自动处理(JWT 模式默认挂载)
手动配置 IHttpContextAccessor + 读取 Claims services.AddHttpContextSetup<AspNetUser>() + 注入 ICurrentUser

v8.0.x → v10.0.x

  • API 无变化。v10.0.x 仅在 net10.0 TFM 上编译,所有公开 API 签名保持一致。
  • 依赖包 Microsoft.AspNetCore.Authentication.JwtBearer 从 8.0.27 升级到 10.0.8。
  • 依赖包 NetDevPack.Security.JwtExtensions 从 8.0.0 升级到 9.0.1。

📄 License

MIT License © RuoVea


🔗 相关资源: NuGet Gallery · 问题反馈

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on RuoVea.ExJwtBearer:

Package Downloads
RuoVea.OmiApi.UserRoleMenu

用户角色菜单管理

RuoVea.OmiApi.SystemApp

系统应用管理

RuoVea.OmiApi.UserRole

用户角色管理

RuoVea.OmiApi.Auth

认证授权模块

RuoVea.OmiApi.User

用户管理模块

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0.6 126 10/27/2026
10.0.0.5 1,431 6/24/2026
10.0.0.4 264 5/28/2026
10.0.0.3 299 1/28/2026
9.0.0.5 274 5/28/2026
9.0.0.4 361 1/28/2026
9.0.0.3 288 1/26/2026
8.0.0.12 133 10/27/2026
8.0.0.11 2,521 6/24/2026
8.0.0.10 362 5/28/2026
8.0.0.9 467 1/28/2026
8.0.0.8 382 1/26/2026
7.0.0.10 326 5/28/2026
7.0.0.9 506 1/28/2026
7.0.0.8 428 1/26/2026
6.0.12.10 365 5/28/2026
6.0.12.9 595 1/28/2026
6.0.12.8 491 1/26/2026
5.0.1.7 139 5/28/2026
5.0.1.6 171 1/28/2026
Loading failed