【问题标题】:Does .Net Core support User Secrets per environment?.Net Core 是否支持每个环境的用户机密?
【发布时间】:2020-06-11 17:02:09
【问题描述】:

假设我在appsettings.Development.json 中指定了开发环境的连接字符串,在appsettings.Staging.json 中指定了暂存环境的连接字符串

在 Development 和 Staging 之间切换所需要做的就是导航到项目属性中的 Visual Studio Debug 选项卡并更改 ASPNETCORE_ENVIRONMENT 环境变量的值。

现在,出于安全原因,我当然不想在appsettings.*.json 中有连接字符串。所以我将其移至用户机密。

问题是 - 似乎只有一个 secrets.json 文件被所有环境使用。没有secrets.Development.jsonsecrets.Staging.json。这意味着在我通过 Visual Studio 调试选项卡从开发环境切换到暂存环境后,我还需要在 secrets.json 中手动更改连接字符串,这违背了为环境提供内置支持的目的。

每个环境不支持用户机密是否正确?如果是这样 - 是否有另一种方法可以避免在切换环境时手动修改 Secret 连接字符串?

【问题讨论】:

  • 不熟悉API,但是可以用docs.microsoft.com/en-us/dotnet/api/…吗?
  • 您可以使用单独的secrets 文件进行调试、发布等
  • 谁投了反对票,你知道你可以为每个配置指定不同的秘密吗?如果不是,为什么要假设每个环境的不同秘密是愚蠢的?
  • @PanagiotisKanavos 为每个配置指定不同秘密的诀窍是什么? --configuration (-c) 参数似乎对dotnet user-secrets 命令没有任何作用。我试过dotnet --version 5.0.403。
  • @BryanKnox 我将其添加到问题中

标签: visual-studio asp.net-core .net-core visual-studio-2019


【解决方案1】:

如果您使用dotnet user-secrets --help 检查工具的参数,您会发现您可以为每个配置(调试、发布、任何其他您想要的)指定不同的秘密,但不能为每个环境指定不同的秘密。如果您考虑一下,这不是个错误的决定。

ASPNETCORE_ENVIRONMENT 环境变量旨在告诉您的应用程序当前机器或容器是开发、生产还是其他环境,因此它可以选择适当的设置文件。预计此环境变量不会从一个应用程序执行更改为下一个应用程序执行。即使在使用容器时,环境变量也会从主机传递到容器,并且不会在容器的生命周期内发生变化。

出于开发目的,secrets 文件应该位于每台机器上,因此无需为每个环境保留单独的文件。使用单独的文件进行配置更有意义,允许开发人员简单地从 Dev 更改为 Release 或 Testing 或他们可能拥有的任何其他自定义配置。

为每个配置指定秘密

dotnet user-secrets 工具的工作原理是从项目文件中读取UserSecretsId 值并将秘密存储在同名的 JSON 文件中,例如c952ecfc-344e-43e1-bb67-1ac05973d6c6.json。可以为每个配置存储一个UserSecretsId

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <UserSecretsId>c952ecfc-344e-43e1-bb67-1ac05973d6c6</UserSecretsId>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <UserSecretsId>7D104000-2230-4EDE-8AE6-63BDDA0BD0C5</UserSecretsId>
</PropertyGroup>

-c参数用于指定配置时,user-secrets工具会从对应的section读取UserSecretsId的值,并用它来存储或读取secret。

dotnet user-secrets init 命令识别-c 参数,所以需要直接修改csproj 文件。

完成后,可以通过指定配置来设置和读取秘密,例如:

❯ dotnet user-secrets set -c Debug Key1 Value1
Successfully saved Key1 = Value1 to the secret store.

❯ dotnet user-secrets set -c Release Key1 Value2
Successfully saved Key1 = Value2 to the secret store.

❯ dotnet user-secrets list -c Debug
Key1 = Value1

❯ dotnet user-secrets list -c Release
Key1 = Value2

【讨论】:

【解决方案2】:

Secret Manager (https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1) 专为开发而设计,而不是任何其他阶段(环境),因为它本质上是不安全的(本地开发机密未加密)。请参阅链接页面上的警告。因此,无需针对该工具存储每个环境的机密信息。对于其他环境(staging、prod 等),Microsoft 可能会引导您使用他们的安全机密存储服务 - Key Vault。您可以将 Secret Manager 用于开发机密,然后将其他环境存储在 Key Vault 中。我在许多 Asp.Net Core 应用程序中都这样做过,而且效果很好。有关 Key Vault 信息,请参阅: https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1

【讨论】:

  • 这不是 OP 所要求的。问题是是否可以通过更改环境变量在开发机器上选择不同的“秘密”文件。
  • 在重读时,也许我理解错了。如果 OP 在同一台机器上拥有这些多个环境,并且想要为所有这些环境使用开发 Secrets Manager,那么我的回答可能不相关(尽管您可以根据需要在本地使用 Key Vault)。
  • @BryanLewis 是的,你误解了我的意思。我知道 Key Vault。我也出于同样的目的使用了 Windows 凭据管理器。
  • 投了赞成票,因为这是很重要的一点。用户 secrets.json 文件仅在 IsDevelopment = true 时与 appsettings.json 合并。因此,您可以通过设置环境在 IDE 中的 appsettings 之间切换,但 secrets 仅限于开发。
  • 您忘记了区域。我们仍然可以在开发环境中为每个区域设置多个单独的密钥。
【解决方案3】:

我也需要这个,我想我想出了一个优雅的解决方案。

secrets.json 文件在您使用的所有环境之间共享,您可以将environment 父级添加到文件中的每个节点,然后执行小技巧(最后 2 个代码片段)。

假设你有一个配置,例如在appsettings.jsonappsettings.{environment}.json

{
  "Key1": "value1",
  "Secret1": "<set yourself>"
}

然后你就有了secrets.json中的秘密部分:

{
  "Secret1": "my secret value"
}

您可以轻松获取并绑定整个部分:

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddJsonFile($"appsettings.{environment}.json")
    .AddUserSecrets<Program>()
    .Build();
var myConfiguration = configuration.Get<MyConfiguration>();

现在问题来了,我想在secrets.json 中有多个环境。我个人曾经对我需要的所有环境都有秘密,只是评论/取消评论我想要的,但是,这是手动工作。所以我会在它们前面加上environment 的名字。

{
  "Development:Secret1": "my secret development value",
  "Staging:Secret1": "my secret staging value"
}

您必须从 IConfiguration 实例加载特定于环境的配置,并使用以下方法覆盖现有的 myConfiguration 值:

configuration.GetSection(environment).Bind(myConfiguration);
\\ or
configurationRoot.Bind(environment, configuration);

就是这样。

如果您使用environment="Development" 运行它,您将加载"my secret development value"。如果您使用environment="Staging" 运行它,您将加载"my secret staging value"

其他细节

双点字符 (:) 充当一个新部分,所以如果你写

{
  "Development:Secret1": "my secret development value",
  "Staging:Secret1": "my secret staging value"
}

和这个一样

{
  "Development":
  {
    "Secret1": "my secret development value"
  },
  "Staging":
  {
    "Secret1": "my secret staging value"
  }
}

诀窍在于仅加载特定于环境的部分并将其绑定到 myConfiguration 实例。

不得不提的是,无论你使用什么环境,所有的秘密实际上都是加载到内存中的。

【讨论】:

  • 优秀的解决方案!谢谢分享。
猜你喜欢
  • 1970-01-01
  • 2017-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多