RuoVea.ExJwtBearer 10.0.0.5

dotnet add package RuoVea.ExJwtBearer --version 10.0.0.5
                    
NuGet\Install-Package RuoVea.ExJwtBearer -Version 10.0.0.5
                    
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.5" />
                    
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.5" />
                    
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.5
                    
#r "nuget: RuoVea.ExJwtBearer, 10.0.0.5"
                    
#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.5
                    
#: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.5
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.ExJwtBearer&version=10.0.0.5
                    
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(如 AddStackExchangeRedisCacheAddDistributedMemoryCache),否则刷新检测将静默失败,导致同一 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.JwtBearer8.0.27 升级到 10.0.8
  • 依赖包 NetDevPack.Security.JwtExtensions8.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.User

用户管理

RuoVea.OmiApi.Auth

认证授权模块

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0.5 143 6/24/2026
10.0.0.4 224 5/28/2026
10.0.0.3 260 1/28/2026
10.0.0.2 210 1/26/2026
9.0.0.5 234 5/28/2026
9.0.0.4 328 1/28/2026
9.0.0.3 245 1/26/2026
8.0.0.11 192 6/24/2026
8.0.0.10 315 5/28/2026
8.0.0.9 434 1/28/2026
8.0.0.8 347 1/26/2026
7.0.0.10 280 5/28/2026
7.0.0.9 468 1/28/2026
7.0.0.8 395 1/26/2026
6.0.12.10 323 5/28/2026
6.0.12.9 558 1/28/2026
6.0.12.8 461 1/26/2026
5.0.1.7 94 5/28/2026
5.0.1.6 137 1/28/2026
5.0.1.5 133 1/26/2026
Loading failed