欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET Core結(jié)合Nacos實(shí)現(xiàn)配置加解密的方法

 更新時(shí)間:2021年06月15日 11:54:45   作者:Catcher8  
當(dāng)我們把應(yīng)用的配置都放到配置中心后,很多人會(huì)想到這樣一個(gè)問題,配置里面有敏感的信息要怎么處理呢?本文就詳細(xì)的介紹了.NET Core Nacos配置加解密,感興趣的可以了解一下

背景

當(dāng)我們把應(yīng)用的配置都放到配置中心后,很多人會(huì)想到這樣一個(gè)問題,配置里面有敏感的信息要怎么處理呢?

信息既然敏感的話,那么加個(gè)密就好了嘛,相信大部分人的第一感覺都是這個(gè),確實(shí)這個(gè)是最簡(jiǎn)單也是最合適的方法。

其實(shí)很多人都在關(guān)注這個(gè)問題,好比說,數(shù)據(jù)庫的連接字符串,調(diào)用第三方的密鑰等等這些信息,都是不太想讓很多人知道的。

那么如果我們把配置放在 Nacos 了,我們可以怎么操作呢?

想了想不外乎這么幾種:

  • 全部服務(wù)端搞定,客戶端只管??;
  • 全部客戶端搞定,服務(wù)端只管存;
  • 客戶端為主,服務(wù)端為輔,服務(wù)端存一些加解密需要的輔助信息即可。

有一個(gè)老哥已經(jīng)在 issue 里面提出了相關(guān)的落地方案,也包含了部分實(shí)現(xiàn)。

https://github.com/alibaba/nacos/issues/5367

簡(jiǎn)要概述的話就是,開個(gè)口子,用戶可以在客戶端拓展任意加解密方式,同時(shí)服務(wù)端可以輔助這一操作。

不過看了 2.0.2 的代碼,服務(wù)端這一塊的“輔助”還未完成,不過對(duì)客戶端來說,這一塊其實(shí)問題已經(jīng)不大了。

6月14號(hào)發(fā)布的 nacos-sdk-csharp 1.1.0 版本已經(jīng)支持了這一功能

下面就用 .NET 5 和 Nacos 2.0.2 為例,來簡(jiǎn)單說明一下。

簡(jiǎn)單原理說明

sdk 里面在進(jìn)行配置相關(guān)讀寫操作的時(shí)候,會(huì)有一個(gè) DoFilter 的操作。這個(gè)操作就是我們的切入點(diǎn)。

既然要執(zhí)行 Filter , 那么執(zhí)行的 Filter 從那里來呢? 答案是 IConfigFilter 。

sdk 里面提供了 IConfigFilter 這個(gè)接口,但是不提供實(shí)現(xiàn),具體實(shí)現(xiàn)交由用戶自定義,畢竟 100 個(gè)人就有 100 種不一樣的實(shí)現(xiàn)。

下面看看它的定義。

public interface IConfigFilter
{
    void Init(NacosSdkOptions options);

    int GetOrder();

    string GetFilterName();
    
    void DoFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain);
}

Init 方法就是對(duì)這個(gè) ConfigFilter 進(jìn)行一些初始化操作,好比說從 Options 里面拿一些額外的信息。

GetOrderGetFilterName 屬于輔助信息,指定這個(gè) ConfigFilter 的執(zhí)行順序(越小越先執(zhí)行)和名稱。

DoFilter 就是核心了,它可以變更 request 和 response ,這兩個(gè)對(duì)象內(nèi)部都會(huì)維護(hù)一個(gè)包含配置信息的 Dictionary。

換言之,只要我們定義一個(gè) ConfigFilter,實(shí)現(xiàn)了這個(gè)接口,那么配置想怎么操作都可以了,加解密就是小問題了。

其中 NacosSdkOptions 里面加了兩個(gè)配置項(xiàng),是專門給這個(gè)功能用的 ConfigFilterAssembliesConfigFilterExtInfo

ConfigFilterAssemblies 是自定義 ConfigFilter 所在的程序集的名字,這里是一個(gè)字符串列表類型的參數(shù),sdk 會(huì)根據(jù)這個(gè)名字去找到對(duì)應(yīng)的實(shí)現(xiàn),然后初始化好。

ConfigFilterExtInfo 是實(shí)現(xiàn) ConfigFilter 是需要用到的擴(kuò)展信息,這里是一個(gè)字符串類型的參數(shù),擴(kuò)展信息復(fù)雜的可以考慮傳入一個(gè) JSON 字符串。

下面來看個(gè)具體的例子吧。

自定義 ConfigFilter

這個(gè) Filter 實(shí)現(xiàn)的效果是把部分敏感配置項(xiàng)進(jìn)行加密,敏感的配置項(xiàng)需要在配置文件中指定。

先是 Init 方法:

public void Init(NacosSdkOptions options)
{
    // 從 Options 里面的拓展信息獲取需要加密的 json path
    // 這里只是示例,根據(jù)具體情況調(diào)整成自己合適的!?。。?
    var extInfo = JObject.Parse(options.ConfigFilterExtInfo);

    if (extInfo.ContainsKey("JsonPaths"))
    {
        // JsonPaths 在這里的含義是,那個(gè)path下面的內(nèi)容要加密
        _jsonPaths = extInfo.GetValue("JsonPaths").ToObject<List<string>>();
    }
}

然后是 DoFilter 方法:

這個(gè)方法里面要注意幾點(diǎn):

  • request 只有請(qǐng)求的時(shí)候才會(huì)有值,其他時(shí)候都是 null 值。
  • response 只有響應(yīng)的時(shí)候才會(huì)有值,其他時(shí)候都是 null 值。
  • 操作完之后,一定要調(diào)用 PutParameter 方法進(jìn)行覆蓋才會(huì)生效。
public void DoFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain)
{
    if (request != null)
    {
        var encryptedDataKey = DefaultKey;
        var raw_content = request.GetParameter(Nacos.V2.Config.ConfigConstants.CONTENT);

        // 部分配置加密后的 content
        var content = ReplaceJsonNode((string)raw_content, encryptedDataKey, true);

        // 加密配置后,不要忘記更新 request !!!!
        request.PutParameter(Nacos.V2.Config.ConfigConstants.ENCRYPTED_DATA_KEY, encryptedDataKey);
        request.PutParameter(Nacos.V2.Config.ConfigConstants.CONTENT, content);
    }

    if (response != null)
    {
        var resp_content = response.GetParameter(Nacos.V2.Config.ConfigConstants.CONTENT);
        var resp_encryptedDataKey = response.GetParameter(Nacos.V2.Config.ConfigConstants.ENCRYPTED_DATA_KEY);

        // nacos 2.0.2 服務(wù)端目前還沒有把 encryptedDataKey 記錄并返回,所以 resp_encryptedDataKey 目前只會(huì)是 null
        // 如果服務(wù)端有記錄并且能返回,我們可以做到每一個(gè)配置都用不一樣的 encryptedDataKey 來加解密。
        // 目前的話,只能固定一個(gè) encryptedDataKey 
        var encryptedDataKey = (resp_encryptedDataKey == null || string.IsNullOrWhiteSpace((string)resp_encryptedDataKey)) 
                ? DefaultKey 
                : (string)resp_encryptedDataKey;

        var content = ReplaceJsonNode((string)resp_content, encryptedDataKey, false);
        response.PutParameter(Nacos.V2.Config.ConfigConstants.CONTENT, content);
    }
}

這里涉及 encryptedDataKey 的相關(guān)操作都只是預(yù)留操作,現(xiàn)階段可以不用理會(huì)。

還有一個(gè) ReplaceJsonNode 方法就是替換敏感配置的具體操作了。

private string ReplaceJsonNode(string src, string encryptedDataKey, bool isEnc = true)
{
    // 示例配置用的是JSON,如果用的是 yaml,這里換成用 yaml 解析即可。
    var jObj = JObject.Parse(src);

    foreach (var item in _jsonPaths)
    {
        var t = jObj.SelectToken(item);

        if (t != null)
        {
            var r = t.ToString();

            // 加解密
            var newToken = isEnc
                ? AESEncrypt(r, encryptedDataKey)
                : AESDecrypt(r, encryptedDataKey);

            if (!string.IsNullOrWhiteSpace(newToken))
            {
                // 替換舊值
                t.Replace(newToken);
            }
        }
    }

    return jObj.ToString();
}

到這里,自定義的 ConfigFilter 已經(jīng)完成了,下面就是真正的應(yīng)用了。

簡(jiǎn)單應(yīng)用

老樣子,建一個(gè) WebApi 項(xiàng)目,添加自定義 ConfigFilter 所在的包/項(xiàng)目/程序集。

這里用的是集成 ASP.NET Core 的例子。

修改 appsettings.json

{
  "NacosConfig": {
    "Listeners": [     
      {
        "Optional": true,
        "DataId": "demo",
        "Group": "DEFAULT_GROUP"
      }
    ],
    "Namespace": "cs",
    "ServerAddresses": [ "http://localhost:8848/" ],
    "ConfigFilterAssemblies": [ "XXXX.CusLib" ],
    "ConfigFilterExtInfo": "{\"JsonPaths\":[\"ConnectionStrings.Default\"],\"Other\":\"xxxxxx\"}"
  }
}

注:老黃這里把 Optional 設(shè)置成 true,是為了第一次運(yùn)行的時(shí)候,如果服務(wù)端沒有進(jìn)行配置而不至于退出程序。

修改 Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}";

        Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
            .MinimumLevel.Override("System", LogEventLevel.Warning)
            .MinimumLevel.Debug()
            .WriteTo.Console(outputTemplate: outputTemplate)
            .CreateLogger();

        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        try
        {
            Log.ForContext<Program>().Information("Application starting...");
            CreateHostBuilder(args, Log.Logger).Build().Run();
        }
        catch (System.Exception ex)
        {
            Log.ForContext<Program>().Fatal(ex, "Application start-up failed!!");
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IHostBuilder CreateHostBuilder(string[] args, Serilog.ILogger logger) =>
        Host.CreateDefaultBuilder(args)
             .ConfigureAppConfiguration((context, builder) =>
             {
                 var c = builder.Build();                    
                 builder.AddNacosV2Configuration(c.GetSection("NacosConfig"), logAction: x => x.AddSerilog(logger));
             })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>().UseUrls("http://*:8787");
            })
            .UseSerilog();
}

最后是 Startup.cs

public class Startup
{
    // 省略部分....
   
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddNacosV2Config(Configuration, null, "NacosConfig");
        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        var configSvc = app.ApplicationServices.GetRequiredService<Nacos.V2.INacosConfigService>();

        var db = $"demo-{DateTimeOffset.Now.ToString("yyyyMMdd_HHmmss")}";
        var oldConfig = "{\"ConnectionStrings\":{\"Default\":\"Server=127.0.0.1;Port=3306;Database=" + db + ";User Id=app;Password=098765;\"},\"version\":\"測(cè)試version---\",\"AppSettings\":{\"Str\":\"val\",\"num\":100,\"arr\":[1,2,3,4,5],\"subobj\":{\"a\":\"" + db + "\"}}}";
        
        configSvc.PublishConfig("demo", "DEFAULT_GROUP", oldConfig).ConfigureAwait(false).GetAwaiter().GetResult();

        var options = app.ApplicationServices.GetRequiredService<IOptionsMonitor<AppSettings>>();

        Console.WriteLine("===用 IOptionsMonitor 讀取配置===");
        Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(options.CurrentValue));
        Console.WriteLine("");

        Console.WriteLine("===用 IConfiguration 讀取配置===");
        Console.WriteLine(Configuration["ConnectionStrings:Default"]);
        Console.WriteLine("");

        var pwd = $"demo-{new Random().Next(100000, 999999)}";
        var newConfig = "{\"ConnectionStrings\":{\"Default\":\"Server=127.0.0.1;Port=3306;Database="+ db + ";User Id=app;Password="+ pwd +";\"},\"version\":\"測(cè)試version---\",\"AppSettings\":{\"Str\":\"val\",\"num\":100,\"arr\":[1,2,3,4,5],\"subobj\":{\"a\":\""+ db +"\"}}}";

        // 模擬 配置變更
        configSvc.PublishConfig("demo", "DEFAULT_GROUP", newConfig).ConfigureAwait(false).GetAwaiter().GetResult();

        System.Threading.Thread.Sleep(500);

        var options2 = app.ApplicationServices.GetRequiredService<IOptionsMonitor<AppSettings>>();

        Console.WriteLine("===用 IOptionsMonitor 讀取配置===");
        Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(options2.CurrentValue));
        Console.WriteLine("");

        Console.WriteLine("===用 IConfiguration 讀取配置===");
        Console.WriteLine(Configuration["ConnectionStrings:Default"]);
        Console.WriteLine("");

        // 省略部分....
    }
}

最后來看看幾張效果圖:

首先是程序的運(yùn)行日志。

其次是和 Nacos 控制臺(tái)的對(duì)比。

到這里的話,基于 Nacos 的加解密就完成了。

寫在最后

敏感配置項(xiàng)的加解密還是很有必要的,配置中心負(fù)責(zé)存儲(chǔ),客戶端負(fù)責(zé)加解密,這樣的方式可以讓用戶更加靈活的選擇自己想要的加解密方法。

本文的示例代碼已經(jīng)上傳到 Github,僅供參考。

https://github.com/catcherwong-archive/2021/tree/main/NacosConfigWithEncryption

最后的最后,希望感興趣的大佬可以一起參與到這個(gè)項(xiàng)目來。

nacos-sdk-csharp 的地址 :https://github.com/nacos-group/nacos-sdk-csharp

到此這篇關(guān)于.NET Core結(jié)合Nacos實(shí)現(xiàn)配置加解密的方法的文章就介紹到這了,更多相關(guān).NET Core Nacos配置加解密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論