聊一聊.NET Core结合Nacos实现配置加解密

背景

当我们把应用的配置都放到配置中心后,很多人会想到这样一个问题,配置里面有敏感的信息要怎么处理呢?

信息既然敏感的话,那么加个密就好了嘛,相信大部分人的第一感觉都是这个,确实这个是最简单也是最合适的方法。

其实很多人都在关注这个问题,好比说,数据库的连接字符串,调用第三方的密钥等等这些信息,都是不太想让很多人知道的。

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

想了想不外乎这么几种:

  1. 全部服务端搞定,客户端只管取;

  2. 全部客户端搞定,服务端只管存;

  3. 客户端为主,服务端为辅,服务端存一些加解密需要的辅助信息即可。

有一个老哥已经在 issue 里面提出了相关的落地方案,也包含了部分实现。

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

简要概述的话就是,开个口子,用户可以在客户端拓展任意加解密方式,同时服务端可以辅助这一操作。

不过看了 2.0.2 的代码,服务端这一块的“辅助”还未完成,不过对客户端来说,这一块其实问题已经不大了。

6月14号发布的 nacos-sdk-csharp 1.1.0 版本已经支持了这一功能

下面就用 .NET 5 和 Nacos 2.0.2 为例,来简单说明一下。

简单原理说明

sdk 里面在进行配置相关读写操作的时候,会有一个 DoFilter 的操作。这个操作就是我们的切入点。

既然要执行 Filter , 那么执行的 Filter 从那里来呢?答案是 IConfigFilter

sdk 里面提供了 IConfigFilter 这个接口,但是不提供实现,具体实现交由用户自定义,毕竟 100 个人就有 100 种不一样的实现。

下面看看它的定义。

public interface IConfigFilter
{void Init(NacosSdkOptions options);int GetOrder();string GetFilterName();void DoFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain);
}

Init 方法就是对这个 ConfigFilter 进行一些初始化操作,好比说从 Options 里面拿一些额外的信息。

GetOrderGetFilterName 属于辅助信息,指定这个 ConfigFilter 的执行顺序(越小越先执行)和名称。

DoFilter 就是核心了,它可以变更 request 和 response ,这两个对象内部都会维护一个包含配置信息的 Dictionary。

换言之,只要我们定义一个 ConfigFilter,实现了这个接口,那么配置想怎么操作都可以了,加解密就是小问题了。

其中 NacosSdkOptions 里面加了两个配置项,是专门给这个功能用的 ConfigFilterAssembliesConfigFilterExtInfo

ConfigFilterAssemblies 是自定义 ConfigFilter 所在的程序集的名字,这里是一个字符串列表类型的参数,sdk 会根据这个名字去找到对应的实现,然后初始化好。

ConfigFilterExtInfo  是实现 ConfigFilter 是需要用到的扩展信息,这里是一个字符串类型的参数,扩展信息复杂的可以考虑传入一个 JSON 字符串。

下面来看个具体的例子吧。

自定义 ConfigFilter

这个 Filter 实现的效果是把部分敏感配置项进行加密,敏感的配置项需要在配置文件中指定。

先是 Init 方法:

public void Init(NacosSdkOptions options)
{// 从 Options 里面的拓展信息获取需要加密的 json path// 这里只是示例,根据具体情况调整成自己合适的!!!!var extInfo = JObject.Parse(options.ConfigFilterExtInfo);if (extInfo.ContainsKey("JsonPaths")){// JsonPaths 在这里的含义是,那个path下面的内容要加密_jsonPaths = extInfo.GetValue("JsonPaths").ToObject<List<string>>();}
}

然后是 DoFilter 方法:

这个方法里面要注意几点:

  1. request 只有请求的时候才会有值,其他时候都是 null 值。

  2. response 只有响应的时候才会有值,其他时候都是 null 值。

  3. 操作完之后,一定要调用 PutParameter 方法进行覆盖才会生效。

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);// 部分配置加密后的 contentvar 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 服务端目前还没有把 encryptedDataKey 记录并返回,所以 resp_encryptedDataKey 目前只会是 null// 如果服务端有记录并且能返回,我们可以做到每一个配置都用不一样的 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 的相关操作都只是预留操作,现阶段可以不用理会。

还有一个 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 已经完成了,下面就是真正的应用了。

简单应用

老样子,建一个 WebApi 项目,添加自定义 ConfigFilter 所在的包/项目/程序集。

这里用的是集成 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 设置成 true,是为了第一次运行的时候,如果服务端没有进行配置而不至于退出程序。

修改 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("");// 省略部分....}
}

最后来看看几张效果图:

首先是程序的运行日志。


其次是和 Nacos 控制台的对比。


到这里的话,基于 Nacos 的加解密就完成了。

写在最后

敏感配置项的加解密还是很有必要的,配置中心负责存储,客户端负责加解密,这样的方式可以让用户更加灵活的选择自己想要的加解密方法。

本文的示例代码已经上传到 Github,仅供参考。

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

最后的最后,希望感兴趣的大佬可以一起参与到这个项目来。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/301640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

给孩子讲100个科学道理,不如带他做这些趣味实验!

▲数据汪特别推荐点击上图进入玩酷屋玩具和学习看似是两个对立的东西&#xff0c;孩子天性爱玩&#xff0c;家长却希望孩子能多学习。不一定非要啃课本才能汲取知识&#xff0c;有时候&#xff0c;在轻松有趣的游戏中也能学到课堂上学不到的知识。让学习变得有趣、高效——给孩…

linux服务器出现黄,linux服务器出现严重故障后的原因以及解决方法

linux服务器出现严重故障后的原因以及解决方法发布时间&#xff1a;2011-11-24 16:32:18 作者&#xff1a;佚名 我要评论linux服务器出现严重故障后的解决方法&#xff0c;本文为大家介绍四个步骤解决linux服务器严重故障。1、把系统安装光盘插入&#xff0c;重启机器&…

史上最污技术解读,我竟然秒懂了!

全世界只有3.14 % 的人关注了数据与算法之美或许有不少人对热备&#xff0c;冷备&#xff0c;云备了解不深。今天&#xff0c;数据汪就给大伙科普一下 IT 行业各种备份术语&#xff0c;以后别闹笑话了。1.冷备份假设你是一位女性&#xff0c;你有一位男朋友&#xff0c;于此同时…

linux免费日志分析工具,分享10个优秀的日志分析工具

原标题&#xff1a;分享10个优秀的日志分析工具在网络管理员的日常工作中&#xff0c;对日志文件的分析能够及时了解网络设备或应用的性能数据&#xff0c;以便及时发现问题并及时解决。分析日志文件的时候需要运用到日志分析工具&#xff0c;接下来小编就给大家分享10个优秀的…

BenchmarkDotNet性能测试

BenchmarkDotNet是一款开源的性能测试工具&#xff0c;使用方式非常简单&#xff0c;特别是对实现同一功能的两种方式&#xff0c;犹豫不决时&#xff0c;可以使用它进行个对比。比如我们比较ADO.NET方式查询数据库表&#xff0c;和用Dapper方式获取数据为表&#xff0c;只需要…

每日一笑 | 程序员的日常,这也太真实了......

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图片来源于网络&#xff0c;侵权删&#xff09;

【转载】分布式系统的理解

2019独角兽企业重金招聘Python工程师标准>>> 1. 如何理解“分布式”&#xff1f; 经常听到”分布式系统“&#xff0c;”分布式计算“&#xff0c;”分布式算法“。分布式的具体含义是什么&#xff1f;狭义的分布是指&#xff0c;指多台PC在地理位置上分布在不同的…

小米开招.NET,20~40k*16薪,点名要求WPF+自动化!

近期小米发布了深圳南山区的C#工程师招聘岗位&#xff0c;20~40k*14薪的待遇让人心动&#xff0c;其核心技术要求再WPF、MVVM、自动化设备、本地数据库等&#xff0c;有没有发现什么秘密&#xff1f;互联网造车&#xff01;物联网终极形态自今年3月份小米宣布要造车&#xff0c…

linux内核线程socket,从Linux源码看Socket(TCP)的accept

从Linux源码看Socket(TCP)的accept前言笔者一直以为若是能知道从应用到框架再到操做系统的每一处代码&#xff0c;是一件Exciting的事情。 今天笔者就从Linux源码的角度看下Server端的Socket在进行Accept的时候到底作了哪些事情(基于Linux 3.10内核)。html一个最简单的Server端…

豆瓣最高评分8.9!让你拥有“多样性思维”

▲数据汪特别推荐点击上图进入玩酷屋01表象与本质我们的大脑是如何工作的&#xff1f;所谓的类比到底是什么?我们是怎么在截然不同的情景间建立起联系的&#xff1f;类比在学习的过程中发挥着怎样的作用&#xff1f;人类大脑中的每个概念都源于多年来不知不觉中形成的一长串类…

复仇!3:1 KO叙利亚!.NET程序员用算法推演出国足进世界杯概率...

6月16日凌晨2点&#xff0c;国足在阿联酋沙迦体育场迎战卡塔尔世预赛亚洲区40强赛最后一个对手&#xff0c;已经锁定小组头名并出线的劲敌叙利亚队&#xff0c;上半场艾克森中框&#xff0c;武磊送助攻&#xff0c;张稀哲打破僵局&#xff0c;下半场奥斯曼扳平比分&#xff0c;…

linux终端背景透明度设置,Ubuntu Terminal标签背景颜色设置

使用ubuntu以来一直苦于terminal标签颜色差距太小&#xff0c;有时候根本不知道自己在哪个标签下&#xff0c;于是在网上搜索如何设置背景颜色可以让标签便于区分&#xff0c;现总结如下&#xff1a;1.打开gtk-widgets.cssvim /usr/share/themes/Ambiance/gtk-3.0/gtk-widgets.…

快速入门人工智能,这波福利不能错过!

不知不觉&#xff0c;寒假就这样溜走了&#xff0c;看着这个寒假养肥的膘&#xff0c;嗯~又是一个充实的假期。不少同学踏上返校之路&#xff0c;迎来新学期&#xff01;新学期&#xff0c;当然少不了小天的陪伴啦&#xff01;早春开学季&#xff0c;小天送福利&#xff0c;福利…

从代码角度揭示:华为鸿蒙的“套壳”真相!

华为鸿蒙操作系统&#xff08;HarmonyOS&#xff09;出来后&#xff0c;互联网上已经吵翻天了&#xff0c;有人认为HarmonyOS是“自主的全场景分布式系统”&#xff0c;是国产之光&#xff0c;另一派则认为HarmonyOS是“Android套壳”&#xff0c;是挂羊头卖狗肉。作为十年老程…

linux进化树分析的软件,一款好用的进化树可视化编辑软件

原标题&#xff1a;一款好用的进化树可视化编辑软件iTOL(Interactive Tree Of Life)是一个在线显示和操作的进化树工具。我们可以添加很多自定义的项目&#xff0c;丰富和完善自己的进化树&#xff0c;比如添加柱状图、蛋白结构域、heatmap、基因平行转移(horizontal gene tran…

每日一笑 | 床上还是桌上,你总得选一样~

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图片来源于网络&#xff0c;侵权删&#xff09;

Redis分布式锁抽丝剥茧

之前码甲哥写了两篇有关线程安全的文章&#xff1a;•你管这叫线程安全&#xff1f;•.NET八股文&#xff1a;线程同步技术解读分布式锁是"线程同步"的延续最近首度应用"分布式锁"&#xff0c;现在想想&#xff0c;分布式锁不是孤立的技能点&#xff0c;这…

让苹果CEO库克折服的程序员仅10岁!?

▲数据汪特别推荐点击上图进入玩酷屋在国外&#xff0c;编程教育课早已普及&#xff0c;美国、英国、新加坡等国家少儿编程已进入小学标准必修课程体系。韩国、日本也相继在2017年和2020年开展一年级至初三的编程教育普及。美国总统孙女&#xff0c;五岁开始学习在电脑上编程最…

WPF Datagrid合并表头的思路

在使用datagrid的时候&#xff0c;有很多情况下&#xff0c;都需要合并表头&#xff0c;多行表头之类的操作。这就需要我们自定义列了。本文给出一个思路&#xff0c;可以实现此需要&#xff0c;只是本人对这个研究不很明白&#xff0c;只是只是实现&#xff0c;仅此而已。下面…

C# 爬虫:疫情实时信息图

运行结果&#xff1a;using System; using System.Drawing; using System.Text; using NSoup; using NSoup.Nodes; using System.IO; using System.Net; using System.Text.RegularExpressions; using System.Windows.Forms;namespace Pneumonia {public partial class MainFor…