如何在 ASP.NET Core 中使用 UserSecrets 讀取保護的設定值 如何在 ASP.NET Core 中使用 UserSecrets 讀取保護的設定值

Published on Friday, June 9, 2023

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,把設定值放在雲端上可以更安全的保管設定值。