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
<PackageReference Include="RuoVea.ExJwtBearer" Version="10.0.0.5" />
<PackageVersion Include="RuoVea.ExJwtBearer" Version="10.0.0.5" />
<PackageReference Include="RuoVea.ExJwtBearer" />
paket add RuoVea.ExJwtBearer --version 10.0.0.5
#r "nuget: RuoVea.ExJwtBearer, 10.0.0.5"
#:package RuoVea.ExJwtBearer@10.0.0.5
#addin nuget:?package=RuoVea.ExJwtBearer&version=10.0.0.5
#tool nuget:?package=RuoVea.ExJwtBearer&version=10.0.0.5
🔐 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.0TFM 上编译,所有公开 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 | Versions 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. |
-
net10.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.8)
- NetDevPack.Security.JwtExtensions (>= 9.0.1)
- Newtonsoft.Json (>= 13.0.4)
- RuoVea.ExConfig (>= 10.0.0.1)
- RuoVea.ExDto (>= 10.0.0.4)
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 |