You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Program.cs中通过Mediator获取Jwt.IssuerSigningKey修复签名密钥不存在报错

问题根因

你当前的报错来自JWT验证配置阶段的硬编码逻辑:你删除了配置文件中的jwtTokenConfig.Secret字段,但在AddJwtBearer配置中仍在尝试读取这个空字段生成签名密钥,导致所有带[Authorize]的接口验证token时找不到签名密钥。
同时你无法在Program.cs的服务注册阶段拿到Mediator实例,是因为服务注册阶段容器还未构建完成,无法解析任何已注册的服务。

解决方案

方案1:启动预加载密钥(推荐,性能最优,适合密钥不频繁变更的场景)

你可以在服务构建完成后、启动运行前,手动创建作用域读取Mediator实例,从数据库拿到密钥后回填到jwtTokenConfig中,原有验证逻辑几乎不用改:

  1. 保留你原来的AddJwtBearer配置代码即可,不用删除IssuerSigningKey的赋值
  2. 在builder.Build()之后、app.Run()之前添加以下代码:
var app = builder.Build();

// 启动预加载JWT签名密钥
using (var scope = app.Services.CreateScope())
{
    var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
    // 调用你已实现的SecretCommand拿密钥
    var secret = await mediator.Send(new SecretCommand());
    // 回填到配置实例中
    jwtTokenConfig.Secret = secret;
}

// 后面再走原有中间件配置,顺序不要变
app.UseAuthentication();
app.UseAuthorization();

app.Run();

方案2:动态解析密钥(适合需要热更新密钥的场景)

如果你的密钥会在线更新,不需要重启服务生效,可以用IssuerSigningKeyResolver委托在每次token验证时动态获取密钥:

  1. 首先注册HttpContext访问器:
// 加到Program.cs服务注册阶段
builder.Services.AddHttpContextAccessor();
  1. 修改AddJwtBearer中的配置,替换原来的IssuerSigningKey硬编码逻辑:
.AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = true;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = jwtTokenConfig.Issuer,
        ValidateIssuerSigningKey = true,
        // 动态解析签名密钥
        IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
        {
            var httpContextAccessor = validationParameters.PropertyBag[nameof(IHttpContextAccessor)] as IHttpContextAccessor;
            var mediator = httpContextAccessor.HttpContext.RequestServices.GetRequiredService<IMediator>();
            var secret = mediator.Send(new SecretCommand()).GetAwaiter().GetResult();
            return new List<SecurityKey> { new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)) };
        },
        ValidateAudience = false,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.FromMinutes(1)
    };
    // 把HttpContextAccessor注入到验证参数中
    x.Events.OnMessageReceived = context =>
    {
        context.Options.TokenValidationParameters.PropertyBag[nameof(IHttpContextAccessor)] = context.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>();
        return Task.CompletedTask;
    };
});

注:该场景建议给密钥加内存缓存,避免每次验证都查询数据库,降低性能损耗。

现有实现的优化点
  • 你现在注册了两次IJwtAuth实现,后注册的会覆盖先注册的,注意按需删除重复注册逻辑
  • JwtAuth类的构造函数中不要直接new DataAcsess,也不要用.Result同步阻塞异步方法,会有死锁风险,建议把DataAcsess注入到构造函数中,密钥读取逻辑改成异步
  • 签名密钥编码不要用ASCII,改用UTF8避免特殊字符乱码导致验证失败
  • 不要吞掉JWT配置阶段的异常,空catch块会导致所有配置错误都被隐藏,排查问题难度极大

内容的提问来源于stack exchange,提问作者RYNEX Akil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 21:27:02