如何在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],防止未授权用户修改受众列表,引发安全风险。
验证方式
- 启动应用,使用初始受众生成JWT,请求受保护端点验证通过。
- 调用
PUT api/jwt-settings/update-audiences接口,传入新的受众列表。 - 使用新受众生成JWT,请求受保护端点验证通过;若旧受众不在新列表中,使用旧JWT会验证失败。
内容的提问来源于stack exchange,提问作者Cavid Haciyev
相关产品推荐
相关产品推荐

