ASP.NET Core 动态认证方案实战:在运行时添加、移除与更新 Authentication Scheme
ASP.NET Core 动态认证方案实战在运行时添加、移除与更新 Authentication Scheme【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore导读ASP.NET Core 的认证系统通常在应用启动阶段通过AddAuthentication()一次性注册好所有认证方案Authentication Scheme。但在多租户、插件化或可热配置的业务场景中我们往往需要在运行时动态地添加、移除认证方案并更新某个方案对应的命名选项named options。本文以 ASP.NET Core 仓库中的DynamicSchemes官方示例为核心完整讲解如何基于IAuthenticationSchemeProvider与IOptionsMonitorCacheT在运行时增删认证方案、动态更新方案选项并结合示例源码剖析其底层实现原理读完即可在自己的项目中复刻这套能力。一、示例概览DynamicSchemes 能做什么示例位于 src/Security/samples/DynamicSchemes其核心演示能力见 README.md包含三点列出全部认证方案运行应用后首页会展示当前系统中注册的所有认证方案运行时新增与移除方案通过页面底部的表单可以新增方案通过每个方案旁的 Remove 按钮可以删除方案运行时更新方案选项借助同一套添加/更新表单可以随时修改某个方案携带的选项消息options message。示例中最值得研究的是 Pages/Index.cshtml展示层与 Controllers/AuthController.cs操作层它们完整演示了添加/移除方案 更新命名选项的动态流程。二、示例的启动配置静态方案的注册尽管示例主打动态它依然遵循 ASP.NET Core 的标准做法在启动阶段先注册两个静态方案作为起点。Startup.cs 中的ConfigureServices如下public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddAuthentication() .AddSchemeSimpleOptions, SimpleAuthHandler(fromStartup1, o o.DisplayMessage I am from startup..) .AddSchemeSimpleOptions, SimpleAuthHandler(fromStartup2, o o.DisplayMessage Me too!); }这里的关键 API 是AddSchemeTOptions, THandler(string scheme, ActionTOptions configureOptions)它一次性完成三件事注册名为fromStartup1、fromStartup2的两个认证方案指定每个方案使用的选项类型SimpleOptions与处理器类型SimpleAuthHandler通过配置委托初始化选项例如o.DisplayMessage I am from startup..。管道配置同样是标准的 MVC 认证流程UseAuthentication/UseAuthorization动态方案与静态方案在管道层面没有区别因为UseAuthentication依据的是方案注册表IAuthenticationSchemeProvider而非某个固定列表。三、自定义方案与选项SimpleOptions / SimpleAuthHandler示例自定义了一套极简的认证方案定义在 SimpleAuthHandler.cspublic class SimpleOptions : AuthenticationSchemeOptions { public string DisplayMessage { get; set; } } public class SimpleAuthHandler : AuthenticationHandlerSimpleOptions { public SimpleAuthHandler(IOptionsMonitorSimpleOptions options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) { } protected override TaskAuthenticateResult HandleAuthenticateAsync() { throw new NotImplementedException(); } }要点解读SimpleOptions继承自AuthenticationSchemeOptions这是所有认证方案选项的基类额外增加了一个业务字段DisplayMessage用于在页面上展示该方案的自定义信息SimpleAuthHandler继承AuthenticationHandlerSimpleOptions构造函数接收IOptionsMonitorSimpleOptions、ILoggerFactory与UrlEncoder由 DI 自动注入HandleAuthenticateAsync()在此处故意抛出NotImplementedException说明该示例的重点不是实现真实的认证逻辑而是演示方案与选项的动态管理机制。真实项目中你可以在此方法内实现 JWT、自定义 Token 等任意认证校验逻辑。从源码结构看这个精简设计使示例把所有注意力集中在方案注册表与选项缓存的运行时操作上这是理解动态方案机制的最小可运行载体。四、核心操作层AuthController 的动态增删与更新Controllers/AuthController.cs 是整个示例的灵魂它只依赖两个抽象服务就完成了全部动态操作public class AuthController : Controller { private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IOptionsMonitorCacheSimpleOptions _optionsCache; public AuthController(IAuthenticationSchemeProvider schemeProvider, IOptionsMonitorCacheSimpleOptions optionsCache) { _schemeProvider schemeProvider; _optionsCache optionsCache; } public IActionResult Remove(string scheme) { _schemeProvider.RemoveScheme(scheme); _optionsCache.TryRemove(scheme); return Redirect(/); } [HttpPost] public async TaskIActionResult AddOrUpdate(string scheme, string optionsMessage) { if (await _schemeProvider.GetSchemeAsync(scheme) null) { _schemeProvider.AddScheme(new AuthenticationScheme(scheme, scheme, typeof(SimpleAuthHandler))); } else { _optionsCache.TryRemove(scheme); } _optionsCache.TryAdd(scheme, new SimpleOptions { DisplayMessage optionsMessage }); return Redirect(/); } }4.1 两个核心抽象的角色分工抽象服务作用本示例中的用法IAuthenticationSchemeProvider认证方案的注册表管理方案的生命周期增、删、查、列举RemoveScheme/AddScheme/GetSchemeAsyncIOptionsMonitorCacheSimpleOptions命名选项的缓存负责具体选项实例的存取与失效TryRemove/TryAdd二者必须配合使用方案注册表决定有哪些方案、每个方案用什么 Handler 类型而选项缓存决定每个方案当前生效的选项值是什么。只改其一都会造成不一致——例如只注册方案却不写入选项Handler 将拿不到期望的配置。4.2 移除方案Remove_schemeProvider.RemoveScheme(scheme); _optionsCache.TryRemove(scheme);两步缺一不可先从方案注册表中摘除该方案使其不再出现在GetAllSchemesAsync()的枚举结果中再从选项缓存中清除对应的命名选项避免内存中残留过期配置。4.3 添加或更新方案AddOrUpdateif (await _schemeProvider.GetSchemeAsync(scheme) null) { // 方案不存在 → 新建 _schemeProvider.AddScheme(new AuthenticationScheme(scheme, scheme, typeof(SimpleAuthHandler))); } else { // 方案已存在 → 只清掉旧选项缓存 _optionsCache.TryRemove(scheme); } _optionsCache.TryAdd(scheme, new SimpleOptions { DisplayMessage optionsMessage });流程分两路添加新方案通过GetSchemeAsync查询方案是否已存在若不存在则构造一个AuthenticationScheme实例名字、显示名均取schemeHandler 类型指定为SimpleAuthHandler并AddScheme注册更新既有方案若方案已存在直接TryRemove其旧选项随后统一用TryAdd把携带新DisplayMessage的SimpleOptions写入缓存。这里AddScheme注册时没有传入选项配置委托而是事后通过IOptionsMonitorCache手工注入选项实例这正是动态与启动时静态注册AddScheme 配置委托两种路径的关键差异前者可以在任意时刻、以任意内容重建选项。五、展示层Index.cshtml 如何读取动态状态Pages/Index.cshtml 通过 Razor 的inject直接注入两个服务实时渲染当前注册表与选项缓存的状态page using Microsoft.AspNetCore.Authentication using Microsoft.Extensions.Options inject IAuthenticationSchemeProvider SchemeProvider inject IOptionsMonitorSimpleOptions Options { ViewData[Title] Home page; } h2Current Authentication Schemes/h2 ul { var schemes await SchemeProvider.GetAllSchemesAsync(); foreach (var scheme in schemes) { li bScheme:/b scheme.Name -- bHandler:/b scheme.HandlerType.Name bDisplayMessage:/b Options.Get(scheme.Name).DisplayMessage a asp-area asp-controllerAuth asp-actionRemove asp-route-schemescheme.NameRemove/a /li } } /ul h2Add or update a scheme:/h2 form asp-controllerAuth asp-actionAddOrUpdate methodpost classform-horizontal roleform div classform-group label classcol-md-2 control-labelScheme/label div classcol-md-10 input typetext namescheme / /div /div div classform-group label classcol-md-2 control-labelOptionsMessage/label div classcol-md-10 input typetext nameOptionsMessage / /div /div div classform-group div classcol-md-offset-2 col-md-10 button typesubmit classbtn btn-defaultAdd/Update/button /div /div /form三个值得注意的细节枚举全部方案SchemeProvider.GetAllSchemesAsync()返回注册表中的所有AuthenticationScheme包括启动时注册的fromStartup1/fromStartup2以及之后动态添加的任意方案读取命名选项Options.Get(scheme.Name)以方案名为 key从IOptionsMonitorSimpleOptions中取回该方案的DisplayMessage——注意页面注入的是IOptionsMonitorT读取端而控制器注入的是IOptionsMonitorCacheT写入端二者读写同一套命名选项存储表单字段名绑定表单的namescheme与nameOptionsMessage会被 MVC 模型绑定到AddOrUpdate(string scheme, string optionsMessage)不区分大小写提交后即触发添加或更新。页面的IndexModelIndex.cshtml.cs本身是空的所有动态数据都来自上述两个注入服务印证了数据即服务状态的设计。六、运行与体验步骤示例是标准 Web 项目见 DynamicSchemes.csproj 与 Program.cs使用Host.CreateDefaultBuilderUseStartupStartup可在仓库内直接运行# 从仓库根目录进入示例目录 cd src/Security/samples/DynamicSchemes dotnet run运行后的体验路径打开首页会看到fromStartup1DisplayMessage: I am from startup..与fromStartup2DisplayMessage: Me too!两个方案以及各自的 Handler 类型名和 Remove 链接在底部表单输入任意方案名如myDynamicScheme与 OptionsMessage点击 Add/Update页面刷新后新方案立即出现在列表中再次用同一方案名提交不同的 OptionsMessage方案仍在但 DisplayMessage 被更新为新值——方案与选项被热更新点击某方案的 Remove 链接方案与其选项被一并移除页面列表随之缩短。整个过程无需重启进程这正是动态方案机制区别于启动期静态注册的核心价值。七、底层原理与延伸思考7.1 方案注册表与选项缓存的运行时可变性从示例可以看出ASP.NET Core 认证体系在运行时是可写的IAuthenticationSchemeProvider的实现维护了一个内存中的方案集合AddScheme/RemoveScheme直接修改该集合之后任何GetSchemeAsync/GetAllSchemesAsync的调用都会看到最新状态IOptionsMonitorCacheT是按名字键控的缓存TryAdd/TryRemove可以即时替换或清除某个命名选项而 Handler 通过基类持有的IOptionsMonitorTOptions读取选项时也能感知到缓存中的最新值。正因如此添加方案 写入选项与移除方案 清除选项必须成对出现示例中的AddOrUpdate与Remove方法就是这一原则的模板化实现。7.2 可以迁移到真实业务的思路示例的SimpleOptions.DisplayMessage只是一个占位字段在真实系统中这套模式可以直接泛化多租户认证把DisplayMessage替换为租户专属的签名密钥、Issuer、Audience 等参数按租户名动态注册/更新 JWT Bearer 方案热配置结合配置中心推送在运行时修改某方案的选项并TryRemove缓存使其立即生效无需重启插件化认证由外部输入决定注册哪些方案避免在启动时写死所有可能值。需要注意的是HandleAuthenticateAsync中的真实认证逻辑仍需自行实现动态机制只负责方案与选项的编排不负责认证算法本身。7.3 阅读指引想深入源码机制可以继续探索以下路径认证抽象与基础类型IAuthenticationSchemeProvider、AuthenticationScheme位于 src/Security/Authentication 下的认证核心程序集Abstractions/Core选项体系IOptionsMonitorCacheT/IOptionsMonitorT属于 Microsoft.Extensions.Options可在运行时按名字读写与失效选项官方文档对认证体系AddAuthentication、Scheme、Handler的权威说明位于仓库根目录 README.md 指向的 ASP.NET Core 官方文档。结语DynamicSchemes示例用最精简的代码展示了 ASP.NET Core 认证体系中被低估的一项能力方案注册表与命名选项缓存都是运行时可变的。通过IAuthenticationSchemeProvider增删方案、通过IOptionsMonitorCacheT更新选项即可在不重启进程的前提下实现认证方案的动态编排。无论是多租户、热配置还是插件化认证需求这套注册表 选项缓存的协同模式都是可以直接复用的基础设施。【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考