CAP项目集成带身份和证书验证的MongoDB

大家好,我是Edison。

最近,在使用CAP事件总线时,碰到了这样一个需求:微服务采用的是MongoDB,而且还是带身份验证 和 SSL根证书验证的。由于目前网上能找到的资料,都是不带身份验证的MongoDB,现在网络信息安全越来越被重视,那么就需要自己研究一番了。

CAP.MongoDB组件

CAP是一个开源的事件总线项目,在.NET社区已经十分流行了,它提供了多种存储方式:MSSQL, MySQL, PgSQL,MongoDB等,这里我们主要关注MongoDB。快速安装CAP.MongoDB组件:

复制代码
PM> Install-Package DotNetCore.CAP.MongoDB

快速集成CAP.MongoDB组件(StartUp.cs):

复制代码
​public void ConfigureServices(IServiceCollection services)
{
    // ...
​
    services.AddCap(x =>
    {
        x.UseMongoDB(opt=>{
            //MongoDBOptions
        });
        // x.UseXXX ...
    });
}

目前CAP提供的Options如下:

也就是说,CAP的Option只提供了一个DatabaseConnection选项,让我们自己构造Mongo链接字符串供CAP使用。

那么,我们就需要准确地构造这个Mongo连接字符串了。

这里,我们以之前分享的一篇文章《在ASP.NET 6中使用工作单元操作MongoDB》为基础,不熟悉的朋友可以先看看这篇文章。

前提条件/准备工作

这里我们假设在appsettings中我们针对MongoDB的配置项如下格式:

复制代码
"MongoDatabaseConfigs": {
    "Servers": "xxx01.server.net,xxx02.server.net,xxx03.server.net",
    "Port": 27017, // optional, default : 27017
    "ReplicaSetName": "myrs",
    "DatabaseName": "TEST_DB",
    "AuthDatabaseName": "admin", // optional, default: admin
    "ApplicationName": "Todo",
    "UserName": "test_dev_user",
    "Password": "test_dev_password",
    "UseTLS": true,  // optional, default : false
    "AllowInsecureTLS": true, // optional, default : true
    "SslCertificatePath": "/etc/pki/tls/certs/MyCustomCA.cer"
}

配置项中给出了UserName、Password 还有 SSL证书的路径,这些都是需要构造到连接字符串中的。当然,在Kubernetes中,都建议放到Secret中去。

核心工作:封装构造连接字符串的方法

这里我们封装一个生成MongoDB连接字符串的静态方法,用于读取appsettings中的配置项,并帮我们生成CAP可以用的MongoDB连接字符串:

复制代码
public static class DbConnUtil
{
    // Const Settings for Mongo
    private const int DEFAULT_CONNECT_TIMEOUT_MS = 10000; // 10s
    private const int DEFAULT_SERVER_SELECTION_TIMEOUT_MS = 5000; // 5s
    private const string DEFAULT_AUTH_MECHANISM = "SCRAM-SHA-256"; // SCRAM-SHA-256
    private const string DEFAULT_READ_PREFERENCE = "primaryPreferred"; // Primary Preferred
    private const string DEFAULT_SSL_INVALID_HOSTNAME_ALLOWED = "true"; // Allow Invalid HostName for SSL

    /// <summary>
    /// 获取MongoDB数据库连接字符串
    /// 需要在配置文件中提前根据指定Key进行设置
    /// </summary>
    public static string GetMongoDbConnectionString(IConfiguration config)
    {
        var servers = config["MongoDatabaseConfigs:Servers"];
        var port = config["MongoDatabaseConfigs:Port"] ?? "27017";
        if (string.IsNullOrWhiteSpace(servers))
            throw new ArgumentNullException("Mongo Servers Configuration is Missing!");
        var mongoServers = servers.Split(',');

        // Basic Auth
        var userName = config["MongoDatabaseConfigs:UserName"];
        var password = config["MongoDatabaseConfigs:Password"];
        if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(password))
            throw new ArgumentNullException("Mongo Account Configuration is Missing!");

        // Uri
        var replicaName = config["MongoDatabaseConfigs:ReplicaSetName"];
        var authDatabaseName = config["MongoDatabaseConfigs:AuthDatabaseName"] ?? "admin";
        var mongoUriBuilder = new StringBuilder();
        mongoUriBuilder.Append($"mongodb://{userName}:{password}@");
        for (int i = 0; i < mongoServers.Length; i++)
        {
            if (i < mongoServers.Length - 1)
            {
                mongoUriBuilder.Append($"{mongoServers[i]}:{port},");
            }
            else
            {
                mongoUriBuilder.Append($"{mongoServers[i]}:{port}/?");
            }
        }

        // Settings
        var applicationName = config["MongoDatabaseConfigs:ApplicationName"];
        mongoUriBuilder.Append($"replicaSet={replicaName}");
        mongoUriBuilder.Append($"&appName={applicationName}");
        mongoUriBuilder.Append($"&authSource={authDatabaseName}");
        mongoUriBuilder.Append($"&authMechanism={DEFAULT_AUTH_MECHANISM}");
        mongoUriBuilder.Append($"&connectTimeoutMS={DEFAULT_CONNECT_TIMEOUT_MS}"); 
        mongoUriBuilder.Append($"&serverSelectionTimeoutMS={DEFAULT_SERVER_SELECTION_TIMEOUT_MS}");
        mongoUriBuilder.Append($"&readPreference={DEFAULT_READ_PREFERENCE}");

        // TLS/SSL Auth
        var useTLS = Convert.ToBoolean(config["MongoDatabaseConfigs:UseTLS"] ?? "false");
        if (useTLS)
        {
            var allowInsecureTls = Convert.ToBoolean(config["MongoDatabaseConfigs:AllowInsecureTLS"] ?? "true");
            var sslCertificatePath = config["MongoDatabaseConfigs:SslCertificatePath"];
            
            mongoUriBuilder.Append($"&ssl={useTLS}");
            mongoUriBuilder.Append($"&net.ssl.CAFile={sslCertificatePath}");
            mongoUriBuilder.Append($"&net.ssl.allowInvalidCertificates={DEFAULT_SSL_INVALID_HOSTNAME_ALLOWED}");
        }

        return mongoUriBuilder.ToString();
    }
}

最终可以生成的连接字符串为:

复制代码
mongodb://test_dev_user:test_dev_password@xxx01:27017.server.net,xxx02.server.net:27017,xxx03.server.net:27017/?replicaSet=myrs&appName=Todo&authSource=admin&authMechanism=SCRAM-SHA-256&connectTimeoutMS=10000&serverSelectionTimeoutMS=5000&readPreference=primaryPreferred&ssl=True&net.ssl.CAFile=/etc/pki/tls/certs/MyCustomCA.cer&net.ssl.allowInvalidCertificates=true

ASP.NET Core集成CAP

这里我们使用刚刚封装的方法来生成Mongo连接字符串,来快速集成CAP:

复制代码
public static IServiceCollection AddApplicationEventBus(this IServiceCollection services, IConfiguration config)
{
    ......
    // CAP EventBus
    services.AddCap(option =>
    {
        // Transport
        option.UseKafka(option =>
        {
            option.Servers = config["EventBusConfigs:KafkaServers"]
                ?? throw new ArgumentException("EventBusConfigs:KafkaServers must be set!");
            option.ConnectionPoolSize = int.Parse(config["EventBusConfigs:CapConnectionPoolSize"]
                ?? ApplicationDefaultSettings.Default_ConnectionPool_Size);
            option.CustomHeaders = e => new List<KeyValuePair<string, string>>
                    {
                        new KeyValuePair<string, string>(Headers.MessageId, SnowflakeId.Default().NextId().ToString()),
                        new KeyValuePair<string, string>(Headers.MessageName, e.Topic)
                    };

            if (Convert.ToBoolean(config["EventBusConfigs:EnableAuthorization"] ?? "false"))
            {
                var userName = config["EventBusConfigs:SaslUserName"];
                var passWord = config["EventBusConfigs:SaslPassword"];

                if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(passWord))
                    throw new ArgumentNullException("Kafka username or password can't be null!");

                option.MainConfig.Add(KafkaMainConfigKey.SECURITY_PROTOCOL, KafkaProtocol.SASL_SSL);
                option.MainConfig.Add(KafkaMainConfigKey.SASL_MECHANISM, KafkaAuthMechanism.PLAIN);
                option.MainConfig.Add(KafkaMainConfigKey.SASL_USERNAME, userName);
                option.MainConfig.Add(KafkaMainConfigKey.SASL_PASSWORD, passWord);

                if (!string.IsNullOrWhiteSpace(config["EventBusConfigs:SslCertificatePath"]))
                    option.MainConfig.Add(KafkaMainConfigKey.SSL_CA_LOCATION, config["EventBusConfigs:SslCertificatePath"]);
                if (!string.IsNullOrWhiteSpace(config["EventBusConfigs:EnableSslCertificateVerification"]))
                    option.MainConfig.Add(KafkaMainConfigKey.ENABLE_SSL_CERT_VERIFICATION, config["EventBusConfigs:EnableSslCertificateVerification"]);
            }
        });
        option.SucceedMessageExpiredAfter = 3600 * 24 * int.Parse(config["EventBusConfigs:CapSuccessMsgExpireDays"];
        // Storage
        option.UseMongoDB(option =>
        {
            option.DatabaseConnection = DbConnUtil.GetMongoDbConnectionString(config);
            option.DatabaseName = config["MongoDatabaseConfigs:DatabaseName"] 
                ?? throw new ArgumentException("MongoDatabaseConfigs:DatabaseName must be set!");
            option.PublishedCollection = "msg.published";
            option.ReceivedCollection = "msg.received";
        });
    });

    ......

    return services;
}

小结

本文我们了解了如何在CAP中集成带基础身份验证(用户名/密码)+SSL根证书验证的MongoDB,方便CAP能够正常连接MongoDB并生成本地消息表,在网络信息安全越来越重视的现在,相信会对你使用CAP+MongoDB有一定帮助!

参考资料

CAP官方文档:https://cap.dotnetcore.xyz/user-guide/en/storage/mongodb/
MongoDB官方文档:https://www.mongodb.com/docs/v5.0/security/


作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

相关推荐
坚定信念,勇往无前12 小时前
mongodb备份的脚本
数据库·mongodb
正在走向自律17 小时前
金仓数据库MongoDB兼容深度解析:多模融合架构与高性能实战
数据库·mongodb·架构·国产数据库·金仓数据库·信创改造
一点晖光2 天前
MongoDB数据迁移方案整理
数据库·mongodb·数据迁移
lhrimperial2 天前
MongoDB核心技术深度解析题
数据库·mongodb
bing.shao3 天前
FerretDB 替换MongoDB符合信创要求
数据库·mongodb
bing.shao3 天前
FerretDB 完美对接 MongoDB
数据库·mongodb
坚定信念,勇往无前6 天前
docker安装mongodb
mongodb·docker·容器
云和数据.ChenGuang8 天前
openEuler系统下安装MongoDB的技术教程
运维·数据库·mongodb·压力测试·运维工程师·运维技术
ChristXlx8 天前
Linux安装MongoDB(虚拟机适用)
linux·mongodb·postgresql
2301_796512528 天前
React Native鸿蒙跨平台开发如何使用MongoDB或Firebase作为后端数据库来存储车辆信息、保养记录和预约信息
数据库·mongodb·react native