拓冰建站拓冰建站
首页 / 资讯中心 / 正文

.NET 运行时 User Secrets 配置提供程序(Microsoft.Extensions.Configuration.UserSecrets)原理与实战

.NET 运行时 User Secrets 配置提供程序Microsoft.Extensions.Configuration.UserSecrets原理与实战【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime导读本文以 .NET 运行时仓库 中 Microsoft.Extensions.Configuration.UserSecrets 组件 的源码与测试为基础系统讲解 User Secrets用户机密配置提供程序的设计原理与使用方式它如何在开发阶段用本地磁盘上的secrets.json覆盖应用配置、UserSecretsId是如何由 MSBuild 自动写入程序集特性、机密文件在 Windows/macOS/Linux 上分别存放在哪里以及如何通过AddUserSecrets扩展方法把机密接入IConfigurationBuilder。读完本文你将掌握 User Secrets 的完整使用姿势与底层实现细节能够正确排查机密未加载类问题。什么是 User Secrets 配置提供程序Microsoft.Extensions.Configuration.UserSecrets是 .NET 配置体系Microsoft.Extensions.Configuration中一个面向开发环境的配置提供程序实现。它的核心思想是把不应提交进源码仓库的敏感配置连接字符串、API Key、账号密码等以 JSON 形式存储在用户主目录下、版本控制之外的本地文件中然后在构建配置时作为覆盖层叠加到现有配置链上。正如该组件的 README 所述它实现的是 ASP.NET Core 应用机密app secrets机制而组件自身定位于贡献门槛 中明确新特性、新 API、缺陷修复与性能改动均可接受API 与功能已成熟但仍会不时扩展。该组件在仓库中的源码组织如下核心扩展方法UserSecretsConfigurationExtensions.cs机密文件路径解析PathHelper.cs程序集级标识特性UserSecretsIdAttribute.cs构建期注入逻辑buildTransitive/Microsoft.Extensions.Configuration.UserSecrets.targets测试套件tests/如何引用与部署从该组件的 csproj 可以看到它的目标框架覆盖了NetCoreAppCurrent、NetCoreAppPrevious、NetCoreAppMinimum、netstandard2.0以及$(NetFrameworkMinimum)因此既可以在现代 .NET 应用中使用也可以被旧框架项目引用。它的部署形态有两种见 README 的 Deployment 小节随 ASP.NET Core 共享框架分发在 ASP.NET Core 应用中无需显式添加 NuGet 引用即可使用以 OOBout-of-bandNuGet 包独立发布Microsoft.Extensions.Configuration.UserSecrets包可被任意项目直接引用例如控制台应用。依赖关系上该包以项目引用形式依赖 Microsoft.Extensions.Configuration.Json 与 Microsoft.Extensions.FileProviders.Physical对非当前目标框架还额外引用Microsoft.Extensions.Configuration.Abstractions与Microsoft.Extensions.FileProviders.Abstractions见 csproj。这从侧面印证User Secrets 的底层就是用物理文件提供程序读取 JSON 配置文件与AddJsonFile共享同一套加载管道。核心 APIAddUserSecrets 扩展方法组件对外暴露的全部 API 都集中在UserSecretsConfigurationExtensions静态类中ref 程序集 是权威的公开 API 清单共有 7 个AddUserSecrets重载全部以IConfigurationBuilder为this参数重载说明AddUserSecretsT()从类型T所在程序集读取UserSecretsIdAttribute机密缺失时静默跳过optional: trueAddUserSecretsT(bool optional)同上可控制是否允许缺失AddUserSecretsT(bool optional, bool reloadOnChange)同上额外支持文件变更时自动重载AddUserSecrets(Assembly assembly)从指定程序集读取特性默认 optionalAddUserSecrets(Assembly assembly, bool optional)从指定程序集读取特性可控 optionalAddUserSecrets(Assembly assembly, bool optional, bool reloadOnChange)完整版程序集 optional reloadOnChangeAddUserSecrets(string userSecretsId)直接指定机密 IDoptional恒为 trueAddUserSecrets(string userSecretsId, bool reloadOnChange)直接指定机密 ID 是否热重载参数语义optional决定当机密缺失时的行为。缺失分两种情况——程序集上没有UserSecretsIdAttribute仅对按程序集查找的重载有效或机密文件不存在。当optional为false时前者抛出InvalidOperationException后者在Build()时因 JSON 文件缺失而抛出FileNotFoundException为true时静默返回不产生任何配置项。默认值均为true。reloadOnChange是否在secrets.json文件变化后自动重新加载配置默认false。userSecretsId唯一标识一个机密集合的字符串。底层用它定位存储目录与文件名详见后文路径解析因此必须是不含非法文件名字符的合法目录名。按程序集查找的调用链以AddUserSecrets(Assembly assembly, bool optional, bool reloadOnChange)为例源码空值校验configuration与assemblyArgumentNullException.ThrowIfNull通过assembly.GetCustomAttributeUserSecretsIdAttribute()反射读取程序集特性若特性存在调用内部方法AddUserSecretsInternal(configuration, attribute.UserSecretsId, optional, reloadOnChange)继续若特性不存在且optional为false抛出InvalidOperationException错误信息来自 Strings.resx 中的Error_Missing_UserSecretsIdAttribute会明确提示检查项目是否设置了UserSecretsId构建属性若已设置请确认引用了本包若特性不存在且optional为true直接返回原 builder不加载任何配置。泛型重载AddUserSecretsT()本质上是AddUserSecrets(typeof(T).Assembly, optional: true, reloadOnChange: false)的语法糖源码。底层加载复用 AddJsonFile所有重载最终汇入AddSecretsFile源码private static IConfigurationBuilder AddSecretsFile(IConfigurationBuilder configuration, string secretPath, bool optional, bool reloadOnChange) { if (string.IsNullOrEmpty(secretPath)) { return configuration; } string? directoryPath Path.GetDirectoryName(secretPath); PhysicalFileProvider? fileProvider Directory.Exists(directoryPath) ? new PhysicalFileProvider(directoryPath) : null; return configuration.AddJsonFile(fileProvider, PathHelper.SecretsFileName, optional, reloadOnChange); }可见它调用的正是Microsoft.Extensions.Configuration.Json包里的AddJsonFile(fileProvider, fileName, optional, reloadOnChange)重载——因此机密文件也遵循 JSON 配置的层级键如Facebook:PLACEHOLDER与AsEnumerable()展开规则。当机密目录不存在时fileProvider为nullAddJsonFile在 optional 模式下会安全跳过。UserSecretsId 的构建期注入要让AddUserSecretsT()正常工作程序集上必须存在UserSecretsIdAttribute。该特性定义在 UserSecretsIdAttribute.cs只能标注在程序集上AttributeTargets.Assembly不可继承、不可重复Inherited false, AllowMultiple false构造时校验 ID 非空暴露只读属性UserSecretsId其 XML 注释明确指出在绝大多数情况下该特性由 NuGet 包内置的 MSBuild targets 在编译期自动生成值来自 MSBuild 属性UserSecretsId。targets 的注入逻辑Microsoft.Extensions.Configuration.UserSecrets.targets 完整内容如下Project xmlnshttp://schemas.microsoft.com/developer/msbuild/2003 PropertyGroup MSBuildAllProjects Condition$(MSBuildVersion) Or $(MSBuildVersion) lt; 16.0$(MSBuildAllProjects);$(MSBuildThisFileFullPath)/MSBuildAllProjects GenerateUserSecretsAttribute Condition$(GenerateUserSecretsAttribute)true/GenerateUserSecretsAttribute /PropertyGroup ItemGroup Condition $(UserSecretsId) ! AND $(GenerateUserSecretsAttribute) ! false AssemblyAttribute IncludeMicrosoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute _Parameter1$(UserSecretsId.Trim())/_Parameter1 /AssemblyAttribute /ItemGroup /Project关键点触发条件项目定义了UserSecretsId属性非空且GenerateUserSecretsAttribute未显式设为false默认开启GenerateUserSecretsAttribute默认为true生成方式向编译项AssemblyAttribute追加一条特性构造参数_Parameter1为$(UserSecretsId.Trim())——注意它会先对 ID 做 Trim 去空白因此即便在 csproj 里写成多行缩进的UserSecretsId也能得到干净的 IDMsBuildTargetTest的测试工程正是用了带换行缩进的值xyz123构建增量友好该文件是 MSBuild 的AssemblyAttribute项由 SDK 负责生成obj/.../AssemblyInfo.cs重复构建不会重复生成测试中专门断言了第二次构建后文件LastWriteTimeUtc不变见下文。props向 IDE 暴露能力同目录下的 Microsoft.Extensions.Configuration.UserSecrets.props 只做一件事向项目注入ProjectCapabilityItemGroup !-- This capability represents the UserSecretsID secrets.json approach to storing local user secrets. -- ProjectCapability IncludeLocalUserSecrets / /ItemGroup这让 Visual Studio 等 IDE 能够识别该项目启用了本地用户机密这一能力从而在 UI 上提供管理用户机密等入口。两个文件通过 csproj 的 Content 项 打包到buildTransitive\netstandard2.0\以及NetFrameworkMinimum、NetCoreAppMinimum对应目录因此引用该包的项目会自动导入 targets 与 props实现零配置注入。机密文件的存储路径解析PathHelperPathHelper.cs 负责把userSecretsId映射为磁盘上的secrets.json完整路径其算法是理解整个机制的关键平台路径规则环境机密文件路径Windows存在APPDATA%APPDATA%\Microsoft\UserSecrets\userSecretsId\secrets.jsonmacOS / Linux存在HOME~/.microsoft/usersecrets/userSecretsId/secrets.json无APPDATA/HOME依次回退Environment.SpecialFolder.ApplicationData、UserProfile最后是DOTNET_USER_SECRETS_FALLBACK_DIR环境变量其中文件名常量定义在源码中internal const string SecretsFileName secrets.json;。源码级解析顺序InternalGetSecretsPathFromSecretsId源码的执行步骤校验 ID为空抛ArgumentException若包含Path.GetInvalidFileNameChars()中的任意字符抛InvalidOperationException错误信息Error_Invalid_Character_In_UserSecrets_Id会精确指出非法字符及其索引Invalid character {0} found in the user secrets ID at index {1}.。这也是为什么UserSecretsId建议使用 GUID 或项目名-随机串这类纯安全字符的原因。确定根目录优先读环境变量APPDATAWindows→HOMEmacOS/Linux。对于 iOS/tvOS/MacCatalystHOME指向应用沙盒容器根目录且不可写因此在NET下会强制把home置为null转而走SpecialFolder回退链。最后兜底是DOTNET_USER_SECRETS_FALLBACK_DIR——这是官方注释明说的逃生舱escape hatch用于自定义存储根目录。根目录仍为空若throwIfNoRoot为trueAddUserSecrets(assembly, optional: false)场景抛InvalidOperationExceptionError_Missing_UserSecretsLocation提示设置DOTNET_USER_SECRETS_FALLBACK_DIR否则返回空字符串上层AddSecretsFile检测到空路径后直接返回、不加载。拼接路径Windows 风格appData非空拼Microsoft\UserSecrets\...Unix 风格拼.microsoft/usersecrets/...。注意大小写差异MicrosoftWindowsvs.microsoftmacOS/Linux这是由该目录历史上先在 Windows 上定义、后在 Unix 上采用点前缀约定的结果。PathHelper.GetSecretsPathFromSecretsId是公开 API恒以throwIfNoRoot: true调用因此即使只是查询路径在完全无法确定存储位置时也会抛异常。测试验证行为即规范组件测试位于 tests/覆盖三个维度1. 配置扩展行为ConfigurationExtensionTest.csConfigurationExtensionTest.cs 通过程序集级[assembly: UserSecretsId(d6076a6d3ab24c00b2511f10a56c68cc)]模拟真实项目验证AddUserSecrets(typeof(...).Assembly)与AddUserSecretsT()都能从程序集特性发现 ID 并读到写入的机密值AddUserSecrets_FindsAssemblyAttribute、AddUserSecrets_FindsAssemblyAttributeFromType程序集无特性时optional: false抛InvalidOperationException且错误信息精确匹配资源字符串AddUserSecrets_ThrowsIfAssemblyAttributeFromType默认optional不抛异常、配置为空AddUserSecrets_DoesNotThrowsIfOptionalByDefault特性存在但secrets.json不存在时optional: false在Build()时抛FileNotFoundExceptionAddUserSecrets_DoesThrowsIfNotOptionalAndSecretDoesNotExist直接传userSecretsId时可读取嵌套键如Facebook:PLACEHOLDER且文件不存在时静默返回nullAddUserSecrets_With_SecretsId_Passed_Explicitly、AddUserSecrets_Does_Not_Fail_On_Non_Existing_File。2. MSBuild 注入MsBuildTargetTest.csMsBuildTargetTest.cs 在临时目录构造一个极简dotnet new风格的 csproj设置了带换行的UserSecretsIdxyz123/UserSecretsId模拟 NuGet 导入 targets 后真实执行dotnet restore与dotnet build断言生成的obj/Debug/tfm/test.AssemblyInfo.cs中包含assembly: Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute(xyz123)二次构建不重新生成该文件增量构建友好LastWriteTimeUtc保持不变。这从端到端角度验证了 targets 中$(UserSecretsId.Trim())的去空白行为与 AssemblyAttribute 注入全链路。测试同时准备了.csproj/.fsproj两套工程F# 用例因上游问题暂被Skip说明该机制对 C#/F# SDK 项目均适用。3. 路径解析PathHelperTest.csPathHelperTest.cs 验证在APPDATA/HOME下得到的路径与根目录 Microsoft/UserSecrets/或.microsoft/usersecrets/ ID secrets.json的期望完全一致Gives_Correct_Secret_Path当 ID 含非法字符Path.GetInvalidPathChars()与Path.GetInvalidFileNameChars()全集时一律抛InvalidOperationExceptionThrows_If_UserSecretId_Contains_Invalid_Characters。使用示例从配置到读取把以上机制串起来一个标准的开发期机密工作流如下。1. 在 csproj 中声明机密 ID一般由 IDE 的管理用户机密功能或dotnet user-secrets init写入PropertyGroup UserSecretsIdmy-app-3f2b1c0d-9a8e-4f6b-8c1d-2e3f4a5b6c7d/UserSecretsId /PropertyGroup2. 写入机密值例如通过dotnet user-secrets set或直接编辑机密文件{ ConnectionStrings:Default: Serverlocalhost;DatabaseDevDb;User Idsa;Passworddev-only-pwd, ExternalApi:Key: sk-dev-xxxx }3. 在程序入口把 User Secrets 接入配置链using Microsoft.Extensions.Configuration; var builder new ConfigurationBuilder() .AddUserSecretsProgram(); // 从 Program 所在程序集的 UserSecretsIdAttribute 读取 ID // 或.AddUserSecrets(my-app-3f2b1c0d-9a8e-4f6b-8c1d-2e3f4a5b6c7d); var configuration builder.Build(); var connStr configuration[ConnectionStrings:Default];加载顺序上AddUserSecrets通常放在环境相关或敏感度较高的提供程序位置使机密值覆盖同键的appsettings.json同时仍可被命令行参数、环境变量等更靠后的提供程序覆盖形成默认值 → 开发机密 → 环境/运行时覆盖的优先级链。常见问题与最佳实践Could not find UserSecretsIdAttribute on assembly程序集上没有特性且使用了optional: false。按 Strings.resx 的提示排查确认项目已设置UserSecretsId属性并确认项目确实引用了本包targets 才会被导入。机密加载为空先检查PathHelper.GetSecretsPathFromSecretsId(id)返回的路径与编辑器打开的secrets.json是否一致——两者由同一算法定位%APPDATA%/HOME不一致如服务账户与交互用户不同时最易出现看不到机密。UserSecretsId 命名仅允许文件名字符推荐用 GUID 或项目名-随机串避免包含/\:*?|等字符PathHelperTest 对非法字符全集做了断言。环境变量被赋值为空字符串路径解析中APPDATA/HOME若存在但为空会被??跳过空串不触发回退需注意 CI/容器环境变量残留。生产环境不要用 User Secrets它存储于本机用户目录、明文 JSON设计目标仅限开发阶段覆盖配置生产机密应使用环境变量、Azure Key Vault、Secret Manager 等方案。reloadOnChange仅对开发调试有帮助生产环境不建议依赖它承载机密热更新。小结Microsoft.Extensions.Configuration.UserSecrets用极简的设计解决了开发期敏感配置的落地问题UserSecretsIdAttribute在编译期由 MSBuild targets 自动注入PathHelper依据APPDATA/HOME/DOTNET_USER_SECRETS_FALLBACK_DIR等线索定位本机密文件AddUserSecrets系列扩展方法复用AddJsonFile管道完成加载——三个环节各司其职配合 测试套件 对异常分支无特性、文件缺失、非法字符、增量构建的严格约束构成了一个成熟、可靠且可预测的开发期配置覆盖方案。理解这条链路后无论是日常使用还是排障你都能迅速定位问题所在。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门