一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - ASP.NET教程 - .NET Core結(jié)合Nacos實(shí)現(xiàn)配置加解密的方法

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

2021-12-15 14:57Catcher8 ASP.NET教程

當(dāng)我們把應(yīng)用的配置都放到配置中心后,很多人會想到這樣一個問題,配置里面有敏感的信息要怎么處理呢?本文就詳細(xì)的介紹了.NET Core Nacos配置加解密,感興趣的可以了解一下

背景

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

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

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

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

想了想不外乎這么幾種:

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

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

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

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

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

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

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

 

簡單原理說明

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

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

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

下面看看它的定義。

public interface IConfigFilter
{
  void Init(NacosSdkOptions options);

  int GetOrder();

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

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

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

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

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

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

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

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

下面來看個具體的例子吧。

 

自定義 ConfigFilter

這個 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 在這里的含義是,那個path下面的內(nèi)容要加密
      _jsonPaths = extInfo.GetValue("JsonPaths").ToObject<List<string>>();
  }
}

然后是 DoFilter 方法:

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

  • request 只有請求的時候才會有值,其他時候都是 null 值。
  • response 只有響應(yīng)的時候才會有值,其他時候都是 null 值。
  • 操作完之后,一定要調(diào)用 PutParameter 方法進(jìn)行覆蓋才會生效。
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 目前只會是 null
      // 如果服務(wù)端有記錄并且能返回,我們可以做到每一個配置都用不一樣的 encryptedDataKey 來加解密。
      // 目前的話,只能固定一個 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)階段可以不用理會。

還有一個 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)用了。

 

簡單應(yīng)用

老樣子,建一個 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)行的時候,如果服務(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\":\"測試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\":\"測試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)行日志。

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

其次是和 Nacos 控制臺的對比。

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

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

 

寫在最后

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

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

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

最后的最后,希望感興趣的大佬可以一起參與到這個項(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)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/catcher1994/p/14882211.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91国内精品久久久久影院优播 | 网址在线观看你懂我意思吧免费的 | 久久无码AV亚洲精品色午夜麻豆 | 天天插伊人 | 色呦呦tv | 996热视频 | 好猛好紧好硬使劲好大刺激视频 | 日本无吗免费一二区 | 香蕉在线精品亚洲第一区 | 亚洲人成影院午夜网站 | 白丝超短裙被输出娇喘不停小说 | 69日本人xxxxxxxx色 | 青草青青在线 | 日本高清在线观看天码888 | 1024在线视频精品免费 | 成在线人免费视频一区二区三区 | 青苹果乐园影院在线播放 | 国产福利一区二区在线精品 | 成人私人影院在线版 | 黑人同学征服教师麻麻 | 果冻传媒在线视频观看免费 | 香蕉eeww99国产精选播放 | 黄网久久 | 成人精品视频一区二区在线 | 日本免费不卡在线一区二区三区 | 91久久综合九色综合欧美98 | 天堂中文在线免费观看 | 国产伦精品一区二区三区免 | 动漫美女人物被黄漫在线看 | 天天草人人草 | 91四虎国自产在线播放线 | 成人伊在线影院 | 国产无限免费观看黄网站 | 寡妇一级毛片 | 成人久久伊人精品伊人 | 动漫精品午夜在线播放 | 91tv破解版不限次数 | 99久久精品免费精品国产 | 我要看免费毛片 | 好大用力深一点女公交车 | 日本国产在线视频 |