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

如何在ASP.NET Core运行时动态更新JWT认证的ValidAudiences

ASP.NET Core 8 运行时动态更新JWT ValidAudiences(无需重启应用)

核心思路

要让JWT中间件实时感知ValidAudiences的变更,核心是避免静态赋值ValidAudiences,转而通过IOptionsMonitor<T>实时读取配置,并利用JWT验证的自定义委托确保每次请求都使用最新的受众列表。同时需要配置可动态修改的数据源(如内存配置)存储受众信息,修改后触发配置变更通知。

具体实现步骤

1. 定义JwtSettings配置类

创建存储JWT配置的实体类,包含支持动态更新的ValidAudiences列表:

public class JwtSettings
{
    public string Issuer { get; set; } = string.Empty;
    public string SecretKey { get; set; } = string.Empty;
    public List<string> ValidAudiences { get; set; } = new List<string>();
}

2. 配置可动态修改的数据源

在Program.cs中初始化内存配置源(替代只读的appsettings.json,支持运行时修改):

var builder = WebApplication.CreateBuilder(args);

// 从appsettings.json读取初始JWT配置
var initialJwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>() ?? new JwtSettings();

// 创建内存配置源,存储可动态修改的JWT设置
var memoryConfig = new MemoryConfigurationSource
{
    InitialData = new Dictionary<string, string>
    {
        ["JwtSettings:Issuer"] = initialJwtSettings.Issuer,
        ["JwtSettings:SecretKey"] = initialJwtSettings.SecretKey
    }
};

// 添加初始受众到内存配置
for (int i = 0; i < initialJwtSettings.ValidAudiences.Count; i++)
{
    memoryConfig.InitialData[$"JwtSettings:ValidAudiences:{i}"] = initialJwtSettings.ValidAudiences[i];
}

// 将内存配置源添加到配置系统
builder.Configuration.Add(memoryConfig);

// 注册内存配置源,供后续更新使用
builder.Services.AddSingleton(memoryConfig);

3. 配置JWT认证中间件

在ConfigureServices中,通过IOptionsMonitor<JwtSettings>实时读取配置,并使用自定义AudienceValidator确保每次请求都验证最新的受众:

builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings"));

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        var jwtSettingsMonitor = builder.Services.BuildServiceProvider().GetRequiredService<IOptionsMonitor<JwtSettings>>();

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettingsMonitor.CurrentValue.SecretKey)),
            ValidateIssuer = true,
            ValidIssuer = jwtSettingsMonitor.CurrentValue.Issuer,
            ValidateAudience = true,
            // 自定义受众验证逻辑:每次请求都获取最新的ValidAudiences
            AudienceValidator = (audiences, token, parameters) =>
            {
                var currentAudiences = jwtSettingsMonitor.CurrentValue.ValidAudiences;
                return audiences.Intersect(currentAudiences).Any();
            },
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };

        // 监听JwtSettings变更,同步更新密钥和发行者(如果需要)
        jwtSettingsMonitor.OnChange(settings =>
        {
            options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(settings.SecretKey));
            options.TokenValidationParameters.ValidIssuer = settings.Issuer;
        });
    });

builder.Services.AddControllers();

4. 创建更新ValidAudiences的API端点

实现一个受保护的端点,用于运行时修改ValidAudiences列表,并触发配置变更通知:

[ApiController]
[Route("api/jwt-settings")]
[Authorize] // 必须添加授权,防止未授权修改
public class JwtSettingsController : ControllerBase
{
    private readonly IOptionsMonitor<JwtSettings> _jwtSettingsMonitor;
    private readonly MemoryConfigurationSource _memoryConfig;
    private readonly IConfigurationRoot _configurationRoot;

    public JwtSettingsController(IOptionsMonitor<JwtSettings> jwtSettingsMonitor, 
                                 MemoryConfigurationSource memoryConfig,
                                 IConfiguration configuration)
    {
        _jwtSettingsMonitor = jwtSettingsMonitor;
        _memoryConfig = memoryConfig;
        _configurationRoot = (IConfigurationRoot)configuration;
    }

    [HttpPut("update-audiences")]
    public IActionResult UpdateValidAudiences([FromBody] List<string> newAudiences)
    {
        if (newAudiences == null || !newAudiences.Any())
        {
            return BadRequest("受众列表不能为空");
        }

        // 清除旧的受众配置项
        var oldAudienceKeys = _memoryConfig.InitialData.Keys.Where(k => k.StartsWith("JwtSettings:ValidAudiences:")).ToList();
        foreach (var key in oldAudienceKeys)
        {
            _memoryConfig.InitialData.Remove(key);
        }

        // 添加新的受众配置项
        for (int i = 0; i < newAudiences.Count; i++)
        {
            _memoryConfig.InitialData[$"JwtSettings:ValidAudiences:{i}"] = newAudiences[i];
        }

        // 触发配置重载,通知IOptionsMonitor更新
        _configurationRoot.Reload();

        return Ok("受众列表更新成功");
    }
}

关键说明

  • AudienceValidator委托:每次JWT验证时都会执行,确保使用最新的ValidAudiences列表,避免了静态赋值的局限性。
  • 内存配置源:默认配置源(如appsettings.json)是只读的,内存配置支持运行时修改,适合存储动态变更的配置项。
  • 配置重载:修改内存配置后调用_configurationRoot.Reload(),触发IOptionsMonitor的变更通知,让JWT中间件感知到最新配置。
  • 端点授权:更新端点必须添加[Authorize],防止未授权用户修改受众列表,引发安全风险。

验证方式

  1. 启动应用,使用初始受众生成JWT,请求受保护端点验证通过。
  2. 调用PUT api/jwt-settings/update-audiences接口,传入新的受众列表。
  3. 使用新受众生成JWT,请求受保护端点验证通过;若旧受众不在新列表中,使用旧JWT会验证失败。

内容的提问来源于stack exchange,提问作者Cavid Haciyev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:22:02