UserSecrets

在前幾天我們討論了 Dotnet 是如何處理 appsettings.json 設定值的不過由於需要進行版本管理,所以私密的資料 可能被外漏出去會造成很大的問題,因此 Dotnet 提供了 Microsoft.Extensions.Configuration.UserSecrets 這個 Package 給我們使用。

這個 Package 用法也非常簡單,Dotnet 會在專案外的特定區域建立一個 secrets.json 用途跟 appsettings.json 一模一樣 都是用來保存設定檔不過因為在專案外所以在 Commit 時不會外洩出去。

首先先建立一個新的 Web 專案

dotnet new web -o HelloSecret

接下來直接使用內建的 Cli,完成後會申請一個 UserSecretsId 並且新增到 PropertyGroup 裡面

dotnet user-secrets init
Set UserSecretsId to '36a926ff-e45b-413b-a0db-9e354da205d0' for MSBuild project
<PropertyGroup>
  <TargetFramework>net7.0</TargetFramework>
  <Nullable>enable</Nullable>
  <ImplicitUsings>enable</ImplicitUsings>
  <UserSecretsId>36a926ff-e45b-413b-a0db-9e354da205d0</UserSecretsId>
</PropertyGroup>

接下來也是需要透過 Cli 設定 Key Value 值,它會去參考剛剛提到的 UserSecretsId 去建立資料來避免多個專案的 secrets.json 互相干擾。

dotnet user-secrets set "JsonSettings:From" "MySecretJsonSettings"
dotnet user-secrets set "JsonSettings:Secret" "MySecret"

接著前往 %APPDATA%\Microsoft\UserSecrets\36a926ff-e45b-413b-a0db-9e354da205d0 會看到剛剛建立的 secrets.json

之後的流程就跟前幾篇的流程一樣了,建立一個 Class 來準備映射並且把資料收集到 ConfigurationBuilder 並建立出 ConfigurationRoot

// SecretSettings.cs
public class SecretSettings
{
    public string From { get; set; }
    public string Secret { get; set; }
}

正常流程下我們需要手動呼叫 AddUserSecrets 將 secret.json 添加到 ConfigurationBuilder,需要注意這個方法會去讀取 Assembly 來選擇正確的 UserSecretsId。

using HelloSecret;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Configuration.AddUserSecrets<Program>();
var app = builder.Build();
app.MapControllers();
app.Run();

不過同樣的建立 Host 時 Dotnet 會自動幫我們呼叫 AddUserSecrets 方法所以不用自己呼叫,可以直接像我們使用 IOptions 時 直接注入 SecretSettings 到 DI 容器內。

using HelloSecret;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.Configure<SecretSettings>(
    builder.Configuration.GetSection("JsonSettings"));
var app = builder.Build();
app.MapControllers();
app.Run();

比較重要的是建立 Host 時會判斷環境變數是否目前是開發環境,也就是只有開發環境下才會自動添加 secret.json

// HostingHostBuilderExtensions.cs

if (env.IsDevelopment() && env.ApplicationName is { Length: > 0 })
{
    var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
    if (appAssembly is not null)
    {
        appConfigBuilder.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange);
    }
}

最後新增一個 Controller 來進行測試

// HelloController.cs

[ApiController]
[Route("[controller]")]
public class HelloController: ControllerBase
{
    private readonly IOptions<SecretSettings> _options;

    public HelloController(IOptions<SecretSettings> options)
    {
        _options = options;
    }

    [HttpGet(Name = "Get")]
    public string Get()
    {
        return _options.Value.From + ":" + _options.Value.Secret;
    }
}

接下來開啟網址 https://localhost:7192/Hello 結果會回傳 MySecretJsonSettings:MySecret 代表我們成功讀取外部的 json 到我們目前的專案內


Summary

今天了解 user-secrets 的使用方式不過就如同之前提到的,這種方式只推薦在開發環境中使用,正式環境的話 建議是搭配 Azure Key-Vault,把設定值放在雲端上可以更安全的保管設定值。