nacos持久化连接mysql数据库sm4加密方案

参考:https://blog.csdn.net/Darlight/article/details/109205665

简介

  • nacos作为一款优秀的注册发现中心和配置管理工具,能够实现微服务配置的热更新,同时从代码中解耦出去,更加自由的控制服务的上线和下线,使所有的操作全部可视化,独立化。
  • nacos官网
  • 但是在nacos原生版本中,nacos持久化连接mysql数据库是通过配置文件读取数据库的ip、port、user及password的。
    这其实是一种不安全的操作,如果数据库权限约束做的不好,那么数据库密码泄露将是一件很严重的事情

修改源码

  • 完成个性化定制,自定义数据库密码的加密方式,其实并不难,只需要修改nacos源码中数据库连接部分即可
  • nacos(1.4.1版本)数据库连接初始化所在的类是com.alibaba.nacos.config.service.DataSource包下的ExternalDataSourceProperties类,完成数据库信息装配的方法是 build 方法
  • 我们需要做的只是在nacos读取到配置文件中的密码之后,并且将属性值赋值到实体类之前将密码解析出来即可,以下是源码以及修改的部分
    List<HikariDataSource> build(Environment environment, Callback<HikariDataSource> callback) {
        List<HikariDataSource> dataSources = new ArrayList<>();
        Binder.get(environment).bind("db", Bindable.ofInstance(this));
        Preconditions.checkArgument(Objects.nonNull(num), "db.num is null");
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(user), "db.user or db.user.[index] is null");
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(password), "db.password or db.password.[index] is null");
        for (int index = 0; index < num; index++) {
            int currentSize = index + 1;
            Preconditions.checkArgument(url.size() >= currentSize, "db.url.%s is null", index);
            DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
            poolProperties.setDriverClassName(JDBC_DRIVER_NAME);
            poolProperties.setJdbcUrl(url.get(index).trim());
            poolProperties.setUsername(getOrDefault(user, index, user.get(0)).trim());
            /* nacos在这里读取数据库密码 */
//---------------------------------------------------------------------------------------
            /*poolProperties.setPassword(getOrDefault(password, index, password.get(0)).trim());*/
            // 在这里可以自定义密码的解密规则,此处采用sm4加密为例,可以替换成自己的解密类
            String key = "86C63180C2806ED1F47B859DE501215B";
            poolProperties.setPassword(
                Sm4Util.decryptEcb(key, getOrDefault(password, index, password.get(0)).trim()));
//-------------------------------------------------------------------------------------------
            HikariDataSource ds = poolProperties.getDataSource();
            ds.setConnectionTestQuery(TEST_QUERY);
            dataSources.add(ds);
            callback.accept(ds);
        }
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(dataSources), "no datasource available");
        return dataSources;
    }

同时这个类中其他所有的属性都可以根据自己的需要去进行修改,也可以自己实现这个实现类的父接口,不过只是修改密码规则的话还是建议直接在这里修改

sm4工具类

依赖:

	<!--sm4加密算法-->
	<dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.66</version>
    </dependency>

代码:

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

@SuppressWarnings("all")	// nacos一键打包时跳过检查,不加可能会报错
public class Sm4Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ENCODING = "UTF-8";
    public static final String ALGORITHM_NAME = "SM4";

    //加密算法/分组加密模式/分组填充方式
    //PKCS5Padding-以8个字节为一组分组加密
    //定义分组加密模式使用:PKCS5Padding

    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";

    //128-32位16进制;256-64位16进制

    public static final int DEFAULT_KEY_SIZE = 128;

    /**
     * 生成ECB暗号
     *
     * @param algorithmName 算法名称
     * @param mode          模式
     * @param key
     * @return
     * @throws Exception
     * @explain ECB模式(电子密码本模式:Electronic codebook)
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    public static byte[] generateKey() throws Exception {
        return generateKey(DEFAULT_KEY_SIZE);
    }

    public static byte[] generateKey(int keySize) throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * sm4加密
     *
     * @param hexKey   16进制秘钥(忽略大小写)
     * @param paramStr 待加密字符串
     * @return 返回16进制的加密字符串
     * @throws Exception
     */
    public static String encryptEcb(String hexKey, String paramStr) throws Exception {
        String cipherText = "";
        // 16进制字符串-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // String-->byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 加密后的数组
        byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
        // byte[]-->hexString
        cipherText = ByteUtils.toHexString(cipherArray);
        return cipherText;
    }

    /**
     * 加密模式之Ecb
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * sm4解密
     *
     * @param hexKey     16进制密钥
     * @param cipherText 16进制的加密字符串(忽略大小写)
     * @return 解密后的字符串
     * @throws Exception
     * @explain 解密模式:采用ECB
     */
    public static String decryptEcb(String hexKey, String cipherText) throws Exception {
        // 用于接收解密后的字符串
        String decryptStr = "";
        // hexString-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // hexString-->byte[]
        byte[] cipherData = ByteUtils.fromHexString(cipherText);
        // 解密
        byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData);
        // byte[]-->String
        decryptStr = new String(srcData, ENCODING);
        return decryptStr;
    }

    /**
     * @param key
     * @param cipherText
     * @return
     * @throws Exception
     */
    public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {

        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * 校验加密前后的字符串是否为同一数据
     *
     * @param hexKey     16进制密钥(忽略大小写)
     * @param cipherText 16进制加密后的字符串
     * @param paramStr   加密前的字符串
     * @return 是否为同一数据
     * @throws Exception
     * @explain
     */
    public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception{
        // 用于接收校验结果
        boolean flag = false;
        // hexString-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // 将16进制字符串转换成数组
        byte[] cipherData = ByteUtils.fromHexString(cipherText);
        // 解密
        byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData);
        // 将原字符串转换成byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 判断2个数组是否一致
        flag = Arrays.equals(decryptData, srcData);
        return flag;
    }

    public static void main(String[] args) throws Exception {
        String key = "86C63180C2806ED1F47B859DE501215B";
        String s = "QAZXSWEDCVFR";
        String jiami = Sm4Util.encryptEcb(key, s);
        System.out.println("加密====" + jiami);
        String jiemi = Sm4Util.decryptEcb(key, jiami);
        System.out.println("解密====" + jiemi);
        byte[] bs = ByteUtils.fromHexString(key);
        System.out.println(Arrays.toString(bs));
        System.out.println(ByteUtils.toHexString(generateKey()));

        String str = "86C63180C2806ED1F47B859DE501215B";
        int byte_len = str.getBytes("utf-8").length;
        int len = str.length();
        System.out.println("字节长度为:" + byte_len);
        System.out.println("字符长度为:" + len);
        System.out.println("系统默认编码方式:" + System.getProperty("file.encoding"));
    }
}

打包发布

  • 修改完源码需要将源码包打包成可以使用的jar包
    在nacos顶级包路径下打开控制台,输入以下命令
mvn -Prelease-nacos -Dmaven.test.skip=true -Drat.skip=true clean install -U
  • (也可以在IDEA的terminal输入命令进行打包操作)

在这里插入图片描述

  • 然后等待打包成功
    打包好的包位置:
    nacos-xxx\distribution\target\nacos-server-xxx.zip(windows)
    nacos-xxx\distribution\target\nacos-server-xxx.tar.gz(linux)

  • 修改配置文件(nacos-server-1.4.1\nacos\conf\application.properties)中的密码,用自己定义的加密方式加密一遍

    #*************** Config Module Related Configurations ***************#
    ### If use MySQL as datasource:
    spring.datasource.platform=mysql
    
    ### Count of DB:
    db.num=1
    
    ### Connect URL of DB:
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    db.user.0=root
    db.password.0=b916909c4b06919ed4435b0d4873ecd1
    
  • 启动nacos,登录到控制台,日志没有报错那么修改nacos密码连接方式就完成了

    参考:https://nacos.io/zh-cn/docs/quick-start.html

    nacos启动:在nacos-server-1.4.1\nacos\bin打开cmd命令窗口,输入命令

    startup.cmd -m standalone
    

    (standalone代表着单机模式运行,非集群模式)

    看到"Nacos started successfully in stand alone mode.”后表示服务已启动

  • 登录到控制台,nacos默认使用8848端口,可通过 http://127.0.0.1:8848/nacos/index.html 进入自带的控制台界面,默认用户名/密码是nacos/nacos

Nacos源码Idea启动流程

下载Nacos源码

这里推荐下载1.4.1版本,2.0.0版本刚刚出来稳定性还不够。

下载地址:https://gitee.com/sss123a/nacos-all-1.4.1?_from=gitee_search

编译运行

下载完成之后,把项目导入到Idea中,找到Nacos的主启动类,如下图所示:

在这里插入图片描述

点击启动,会出现缺少com.alibaba.nacos.consistency.entity的错误。

出现的原因是因为nacos中用的是proto文件,而代码中没有生成对应的实体类,因此我们需要手动生成。方式如下。

安装protoc

protoc安装包下载地址(https://github.com/protocolbuffers/protobuf/releases),目前最新版本是3.17.0,下载方式点击对应的操作系统的zip包即可。windows版本下载如下:

在这里插入图片描述

下载完成之后,解压到自己喜欢的目录。然后将目录地址下的bin文件夹配置到系统环境变量(path)中,配置完成之后在cmd中输入【protoc --version】检查是否配置成功。

反编译成对应的java实体类

进入到源码的consistency/src/main/proto/路径下,使用cmd命令,依次运行如下指令

protoc --java_out=../java/ ./consistency.proto
protoc --java_out=../java/ ./Data.proto

执行完成之后,可以在com.alibaba.nacos.consistency包下面看到对应的entity包,如下所示:

在这里插入图片描述

启动运行

配置数据库信息

配置方式和nacos软件启动的时候是一样的,首先需要一个本地MySQL数据库,建立数据库nacos_config,如果之前配置过可以不用配置。

  • 在本地数据库新建数据库:nacos
  • 初始化数据库:在nacos数据库中执行 distribution/conf/nacos-mysql.sql 数据库脚本

修改项目中application.properties文件中的数据库连接部分,需要修改distribution.conf文件和console项目中resources下的application.properties,如下图示:

#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=b916909c4b06919ed4435b0d4873ecd1

配置单机启动参数

在Nacos应用程序的配置参数中添加:

-Dnacos.standalone=true -Dnacos.home=E:\code\project\source\nacos\nacos-1.4.1\distribution

第一个参数含义为单机模式启动,第二个参数为nacos.home地址(源码中distribution文件夹的位置)不可以配置错误。

在这里插入图片描述

配置完成之后启动即可,启动成功日志如下:

在这里插入图片描述

### 回答1: 要使用MySQL 8.作为Nacos持久化存储,需要进行以下步骤: 1. 安装MySQL 8.,并创建一个新的数据库和用户。 2. 下载Nacos的最新版本,并解压缩到本地。 3. 进入Nacos的conf目录,编辑application.properties文件,将以下配置添加到文件末尾: ``` spring.datasource.platform=mysql db.num=1 db.url.=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=100&socketTimeout=300&autoReconnect=true db.user=root db.password=123456 ``` 其中,db.url.是MySQL数据库连接地址,db.user和db.password是数据库的用户名和密码。 4. 启动Nacos服务,它将自动使用MySQL 8.作为持久化存储。 5. 如果需要更改数据库配置,可以编辑application.properties文件并重新启动Nacos服务。 注意:在使用MySQL 8.时,需要将MySQL的密码加密方式设置为mysql_native_password。可以使用以下命令更改用户的密码加密方式: ``` ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'new_password'; ``` 其中,'root'是MySQL的用户名,'new_password'是新的密码。 ### 回答2: Nacos是一款分布式的配置中心和元数据管理系统,它支持将配置数据持久化数据库中,而MySQL8.0是一款开源的关系型数据库。 将Nacos配置持久化MySQL8.0中,需要进行以下步骤: 1. 准备MySQL8.0数据库(可以使用docker快速搭建数据库环境),并创建一个新的数据库和相关的用户及权限。 2. 下载nacos-server版本,并进行解压和配置。 3. 打开nacos/conf/nacos-mysql.sql文件,将其中的sql脚本在新建的数据库中执行,以创建必要的表结构和索引。 4. 配置nacos/conf/application.properties文件,修改如下配置: ```java #启用MySQL持久化配置 spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://localhost:3306/nacos?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=GMT%2B8&useSSL=false db.user=root db.password=root ``` 其中,db.url.0需要修改为MySQL连接地址,db.user和db.password根据实际情况进行修改。 5. 启动Nacos服务,使用浏览器访问http://localhost:8848/nacos,输入用户名和密码进行登录。 以上步骤完成后,Nacos就会将所有的配置保存到MySQL数据库中,以保证数据的持久化。 需要注意的是,MySQL8.0与之前版本略有不同,需要修改部分配置项,例如url中添加serverTimezone=GMT%2B8选项,否则会出现时间解析异常的问题。此外,在配置MySQL的时候,建议采用UTF-8编码,以支持中文等多语言配置。 总的来说,使用MySQL8.0作为Nacos持久化存储是十分可行的,而且功能也十分强大,能够满足大部分企业的需求。 ### 回答3: Nacos是一款分布式的服务发现和配置管理平台,它的作用是帮助开发者更好地管理和维护配置文件,同时提供了高可用性、高可扩展性、高性能和易于维护等优势。而Nacos持久化配置则可以将配置信息持久化数据库中,以保证配置信息的安全性和数据可靠性。在这里我们将介绍如何使用MySQL8.0来实现Nacos持久化配置。 首先,我们需要配置MySQL8.0的相关参数: 1、创建一个新的数据库实例,并分配一个新的用户和密码。 2、设置MySQL的字符集为UTF-8,以免出现中文乱码的问题。 3、创建一个新的数据表用于存储配置信息,数据表的结构可以参考Nacos的GitHub仓库。 接下来,我们需要在Nacos的配置文件中进行以下配置: 1、在nacos-server.jar所在的目录下创建一个新的文件夹,例如”nacos-data”。 2、修改nacos的配置文件,指定nacos持久化位置为“nacos-data”文件夹,并设置相应的数据库连接参数。 3、启动nacos,创建需要的配置信息,通过设置数据库连接参数,将配置信息持久化MySQL数据库中。 4、启动nacos,从MySQL数据库中读取配置信息。在启动时,nacos会先从MySQL数据库中加载配置信息,然后再将它们加载到内存中。 总之,Nacos持久化配置功能可以使开发者以更加安全、可靠和高效的方式管理和维护配置信息。通过MySQL8.0的配置,我们可以将配置信息存储在数据库中,并通过nacos的自动读取和加载配置信息,保证了数据的可靠性、安全性和高性能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值