自定义 JsonConfigurationProvider - ASP.NET Core 使用错误的实现

Sha*_*iel 7 c# asp.net-core asp.net-core-2.0

我试图实现一个名为“CryptographyConfigProvider”的自定义 JsonConfigurationProvider,如果它是加密的,它会从流中解密 JSON。

我从 JsonConfigurationProvider 继承并实现了自定义加载方法。

我在 Program.cs 中这样使用它:

  var configuration = new ConfigurationBuilder()
                .AddEncryptedJsonFile($"appsettings.{enviromentValue}.json", optional: true, reloadOnChange: false)
                .Build();
Run Code Online (Sandbox Code Playgroud)

此调用执行我的自定义实现。(见下文)

但是在此 ASP.NET Core 尝试再次访问 appsettings 文件之后:

 webHostBuilder.Build().Run();
Run Code Online (Sandbox Code Playgroud)

异常表明调用的是普通的 JsonConfigurationProvider 而不是我继承的类 CryptographyConfigProvider。

System.FormatException: Could not parse the JSON file. Error on line number '0': 'EncryptedString'. 
---> Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: S. Path '', line 0, position 0.
at Newtonsoft.Json.JsonTextReader.ParseValue()
at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings)
at Microsoft.Extensions.Configuration.Json.JsonConfigurationFileParser.Parse(Stream input)
at Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider.Load(Stream stream)
--- End of inner exception stack trace ---
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load()
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
at Main(String[] args) in Program.cs
Run Code Online (Sandbox Code Playgroud)

有人知道为什么 ASP.NET Core 使用普通的 JsonConfigurationProvider 吗?

这是我的实现:

public static class DecryptionConfigProviderExtension
{
    public static IConfigurationBuilder AddEncryptedJsonFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
    {
        return builder.AddJsonFile(s =>
        {
            s.FileProvider = null;
            s.Path = path;
            s.Optional = optional;
            s.ReloadOnChange = reloadOnChange;
            s.ResolveFileProvider();
        });
    }

    public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<CryptographyConfigurationSource> configureSource) => builder.Add(configureSource);
}

public class CryptographyConfigurationSource : JsonConfigurationSource, IConfigurationSource
{
    public override IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        EnsureDefaults(builder);
        return new CryptographyConfigProvider(this);
    }
}

public class CryptographyConfigProvider : JsonConfigurationProvider
{
    private const string EncryptionKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";

    private AesCryptography _aesCryptography;

    public CryptographyConfigProvider(CryptographyConfigurationSource cryptographyConfigurationSource) : base(cryptographyConfigurationSource)
    {
        _aesCryptography = new AesCryptography();
    }

    public override void Load(Stream stream)
    {
        Data = UnencryptConfiguration(stream);
    }

    private IDictionary<string, string> UnencryptConfiguration(Stream stream)
    {
        var reader = new StreamReader(stream);
        var text = reader.ReadToEnd();
        var jsonString = DecryptIfEncrypted(text);

        using (MemoryStream jsonStream = new MemoryStream())
        {
            var parser = new JsonConfigurationFileParser();
            StreamWriter writer = new StreamWriter(jsonStream);
            writer.Write(jsonString);
            writer.Flush();
            jsonStream.Position = 0;
            return parser.Parse(jsonStream);
        };
    }

    private string DecryptIfEncrypted(string text)
    {
        var jsonString = string.Empty;

        try
        {
            jsonString = _aesCryptography.DecryptString(text, EncryptionKey);
        }
        catch
        {
            jsonString = text;
        }

        return jsonString;
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*son 5

从 .NET Core 2.0 开始,appsettings.{env.EnvironmentName}.json会自动为您加载。如果您对其进行了加密,那么框架在解析它时可能会出现问题。

.ConfigureAppConfiguration((hostingContext, config) =>
{
    ...

    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
          .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

    ...
Run Code Online (Sandbox Code Playgroud)

MetaPackages/src/Microsoft.AspNetCore/WebHost.cs

我会尝试将您的文件命名为其他名称。

我的团队最近实施的另一种解决方案是将机密移至 app.config 并使用受保护的配置对其进行加密。自定义配置提供程序读取应用程序设置(例如Azure:ApiKey)并将它们提供给核心框架。