SpringBoot配置mysql加密之Druid方式

一、导入Druid依赖

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.22</version>
</dependency>

二、生成密文

方式1. 找到存放druid jar包的目录

1-1、在目录栏执行cmd,调出命令行窗口

在这里插入图片描述

1-2、在命令行窗口执行
java -cp druid-1.1.22.jar com.alibaba.druid.filter.config.ConfigTools 数据库密码## 如下
E:\java\.m2\repository\com\alibaba\druid\1.1.22> java -cp .\druid-1.1.22.jar com.alibaba.druid.filter.config.ConfigTools ltkj.test
privateKey:MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAsQxrpzOi7BscR8nc5YIxRS8XUk3vN92V1wMUqu5EOKKr9D9MGVCZ3+7REzTB2uHUbRZgd4oCu7XwuLAx3GdOvwIDAQABAkAVDS2FuYGPQl/39zwhO/xAxisnOaYOhfXplW+xLViGRTqC4dTQ2h2LVMGpYhOPXCMNeMAO6ZhH4mSqNNMs16ClAiEAwaV1H8gjwFAfpMNhX76YjhtNkyt9dc8A2NbCqYvi6rMCIQDqDshX465wOSbnpXacW/8Uwi2Ku727YNFDKXJ9I6oBxQIgbjf0wFA0OSPhvvAOHmbnggr8ToX0dPeLreAfEE20rI0CIQCLSZrOOu9/V3OgnSZV7KWDW/8wNYO2s+o0tsCsWgH9JQIgPzjgmBX8s5CZwzxd2JIxAjH761CdNhTAmZjPPYuNLJg=
publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALEMa6czouwbHEfJ3OWCMUUvF1JN7zfdldcDFKruRDiiq/Q/TBlQmd/u0RM0wdrh1G0WYHeKAru18LiwMdxnTr8CAwEAAQ==
password:omjlkBmUL7vXvuiOUEsYBNejb9j1GuD7aCQWxWLSR9LohB5H7M+X8LR63wBUeyws7yRPNq/zejMAsZ82DX3z1A==PS E:\java\.m2\repository\com\alibaba\druid\1.1.22> 

复制password和publickey

方式2. 工具类生成
利用工具类生成加密后密码和公钥
public static void main(String[] args) throws Exception {String password = "root123";System.out.println("明文密码: " + password);String[] keyPair = ConfigTools.genKeyPair(512);//私钥String privateKey = keyPair[0];//公钥String publicKey = keyPair[1];//用私钥加密后的密文password = ConfigTools.encrypt(privateKey, password);System.out.println("privateKey:" + privateKey);System.out.println("publicKey:" + publicKey);System.out.println("password:" + password);String decryptPassword = ConfigTools.decrypt(publicKey, password);System.out.println("解密后:" + decryptPassword);}

三、项目配置

修改yml配置文件

1. filters添加config
2. 配置解密,同时指定公钥

2-1. 生产环境 建议如下配置
启动时,通过java -jar 启动命令,指定spring.druid.publickey的值,如(java -jar xx.jar --spring.druid.publickey=公钥),
避免通过yml获取到公钥。
2-2 . 开发环境可将公钥直接配置在idea启动参数内。

connectionProperties: config.decrypt=true;config.decrypt.key=${spring.druid.publickey}
datasource:username: rootpassword: RuacV1QzH80HVwZpR5MqagLoOWbRPYPj+yXKJrfEXJxIVchnWfGpdi2PTJCAlWoi7hNN+y4hhDmiGEvdYscW4w==url: jdbc:mysql://localhost:3306/article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#druid 数据源专有配置druid:#配置druid监控页面stat-view-servlet:enabled: trueurl-pattern: /druid/*loginUsername: adminloginPassword: 123456initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: false# 打开PSCachepoolPreparedStatements: true#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果运行时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority#则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4jDruidDataSourceFactoryfilters: stat,wall,config#指定每个连接上PSCache的大小maxPoolPreparedStatementPerConnectionSize: 20#合并多个DruidDataSource的监控数据useGlobalDataSourceStat: true#通过connectProperties属性来打开mergeSql功能;慢SQL记录;配置数据库密码解密;connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000;config.decrypt=true;config.decrypt.key=${spring.druid.publickey}

Note: 需要注意的是

druid是用私钥加密,公钥解密的,即如果公钥为所有人可见,那么所有人均可以解密。因此,上述配置若在团队内可见,没什么影响,但是如果配置文件万一被放到了公网上,相当于把钥匙插在锁上,白加密了。。在生产环境下,建议改为如下配置:

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverdruid:url: jdbc:mysql://localhost:3306/boost-admin?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=falseusername: rootpassword: D/pBfX7uKgNrFNtVbvf5pevX5JAcBbzisC/4JK3hTN5Xty3sm8zWtSjd9TwggT/phP8Ob0wg1qZRVolxmd/39g==# encrypt configfilters: configconnect-properties:config.decrypt: trueconfig.decrypt.key: ${spring.datasource.druid.publickey}mybatis:mapperLocations: classpath:mapper/*.xmltypeAliasesPackage: com.heartsuit.springbootmybatis.oa.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl

一行之差:

config.decrypt.key: ${spring.datasource.druid.publickey},

这里${spring.datasource.druid.publickey}可以有两种传入方式:

通过服务启动参数

java -jar xxx.jar --spring.datasource.druid.publickey=
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIYYdcdptMU6n/4wtb7StmX4LFvmlw7+b5KHm7L8C0txn1+iMeXz3FM7emkGkKMuaLd9OazkjgxNpPCDRaNM7ecCAwEAAQ==

通过系统环境变量
这个可以通过Idea IDE传入(开发环境),或者实际的系统环境变量传入(生产环境)。

总结

实现对配置文件敏感数据的加密,方式很多,但一定要注意安全性,不可以把公钥公开配置在文件中;还是开头那句话:

一把插着钥匙的锁,能说它是安全的吗?

在这里插入图片描述

在这里插入图片描述

四、ConfigFilter解密原理分析

对于上面为何通过dataSource.setFilters(“config”)一行代码就能实现数据库密码的解密功能,你心中是否有疑惑,它具体又是如何配置了一个ConfigFilter实例的呢?带着这个疑问,我们看下DruidDataSource类中两个重要的方法入手:setFilters和setConnectionproperties,通过这两个入口方法找到与数据库连接密码解密有关的源码实现

3.1 DruidDataSource#setFilters方法

public void setFilters(String filters) throws SQLException {if (filters != null && filters.startsWith("!")) {filters = filters.substring(1);this.clearFilters();}this.addFilters(filters);}public void addFilters(String filters) throws SQLException {if (filters == null || filters.length() == 0) {return;}// 多个filter通过逗号分隔String[] filterArray = filters.split("\\,");for (String item : filterArray) {FilterManager.loadFilter(this.filters, item.trim());}}

在上面的addFilters方法中会去遍历配置的filter数组并调用FilterManager#loadFilter方法加载过滤器

3.2 FilterManager类静态代码块
而在FilterManager类中有这样一段静态代码

static {try {Properties filterProperties = loadFilterConfig();for (Map.Entry<Object, Object> entry : filterProperties.entrySet()) {String key = (String) entry.getKey();if (key.startsWith("druid.filters.")) {String name = key.substring("druid.filters.".length());aliasMap.put(name, (String) entry.getValue());}}} catch (Throwable e) {LOG.error("load filter config error", e);}}

上面这段静态代码首先会去调用无参的loadFilterConfig方法加载过滤器配置

public static Properties loadFilterConfig() throws IOException {Properties filterProperties = new Properties();loadFilterConfig(filterProperties, ClassLoader.getSystemClassLoader());loadFilterConfig(filterProperties, FilterManager.class.getClassLoader());loadFilterConfig(filterProperties, Thread.currentThread().getContextClassLoader());return filterProperties;}

而上面的静态方法中又会去调用带两个参数的loadFilterConfig方法,加载druid.jar包中类路径下的META-INF/druid-filter.properties属性配置文件

我们来看下druid-filter.properties文件中有哪些过滤器

druid.filters.default=com.alibaba.druid.filter.stat.StatFilter
druid.filters.stat=com.alibaba.druid.filter.stat.StatFilter
druid.filters.mergeStat=com.alibaba.druid.filter.stat.MergeStatFilter
druid.filters.counter=com.alibaba.druid.filter.stat.StatFilter
druid.filters.encoding=com.alibaba.druid.filter.encoding.EncodingConvertFilter
druid.filters.log4j=com.alibaba.druid.filter.logging.Log4jFilter
druid.filters.log4j2=com.alibaba.druid.filter.logging.Log4j2Filter
druid.filters.slf4j=com.alibaba.druid.filter.logging.Slf4jLogFilter
druid.filters.commonlogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.commonLogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.wall=com.alibaba.druid.wall.WallFilter
druid.filters.config=com.alibaba.druid.filter.config.ConfigFilter
druid.filters.haRandomValidator=com.alibaba.druid.pool.ha.selector.RandomDataSourceValidateFilter

可以看到总共有13个过滤器,ConfigFilter类对应的key为druid.filters.config
然后我们回到最上面的静态代码块中可以看到程序会遍历加载并读取druid-filter.properties文件中属性变量后返回的filterProperties, 并将其中的key截取掉druid.filters.前缀后的字符串作为name和过滤器的全类名作为键值对保存在ConcurrentHashMap<String, String>数据结构的aliasMap属性中。

3.3 FilterManager#loadFilter方法
————————————————

public static void loadFilter(List<Filter> filters, String filterName) throws SQLException {if (filterName.length() == 0) {return;}String filterClassNames = getFilter(filterName);if (filterClassNames != null) {for (String filterClassName : filterClassNames.split(",")) {if (existsFilter(filters, filterClassName)) {continue;}Class<?> filterClass = Utils.loadClass(filterClassName);if (filterClass == null) {LOG.error("load filter error, filter not found : " + filterClassName);continue;}Filter filter;try {filter = (Filter) filterClass.newInstance();} catch (ClassCastException e) {LOG.error("load filter error.", e);continue;} catch (InstantiationException e) {thrownew SQLException("load managed jdbc driver event listener error. " + filterName, e);} catch (IllegalAccessException e) {thrownew SQLException("load managed jdbc driver event listener error. " + filterName, e);} catch (RuntimeException e) {thrownew SQLException("load managed jdbc driver event listener error. " + filterName, e);}filters.add(filter);}return;}if (existsFilter(filters, filterName)) {return;}Class<?> filterClass = Utils.loadClass(filterName);if (filterClass == null) {LOG.error("load filter error, filter not found : " + filterName);return;}try {Filter filter = (Filter) filterClass.newInstance();filters.add(filter);} catch (Exception e) {thrownew SQLException("load managed jdbc driver event listener error. " + filterName, e);}}
————————————————

上面这个方法的目的就是去根据配置的filterName去aliasMap中找到全类名,然后使用类加载器根据filter的全类名加载Filter类并实例化,完成实例化后将Filter类实例添加到DruidDataSource类List数据结构的filters属性中;当然这个过程首先会去判断filters中是否已经有了配置的Filter类实例,有的化则无需再次加载和实例化。

3.4 数据库连接密文解密的具体实现
在ConfigFilter类中有个init方法,正是在这个初始化方法中完成了数据源加密密码的解密

public void init(DataSourceProxy dataSourceProxy) {// 传入的dataSourceProxy就是我们的DruidDatasource实例  if (!(dataSourceProxy instanceof DruidDataSource)) {LOG.error("ConfigLoader only support DruidDataSource");}// DruidDataSource 转 DruidDataSourceDruidDataSource dataSource = (DruidDataSource) dataSourceProxy;// 获取数据源中的连接属性Properties connectionProperties = dataSource.getConnectProperties();// 加载连接属性中配置的加密属性文件Properties configFileProperties = loadPropertyFromConfigFile(connectionProperties);// 判断是否需要解密,如果需要就进行解密boolean decrypt = isDecrypt(connectionProperties, configFileProperties);if (configFileProperties == null) {if (decrypt) {decrypt(dataSource, null);}return;}if (decrypt) {decrypt(dataSource, configFileProperties);}try {DruidDataSourceFactory.config(dataSource, configFileProperties);} catch (SQLException e) {thrownew IllegalArgumentException("Config DataSource error.", e);}}
1

上面这个ConfigFilter#init方法是在DruidDatasource#init方法中触发的

for (Filter filter : filters) {filter.init(this);
}

loadPropertyFromConfigFile方法源码

publics tatic final String CONFIG_FILE = "config.file";
public static final String SYS_PROP_CONFIG_FILE = "druid.config.file";Properties loadPropertyFromConfigFile(Properties connectionProperties) {String configFile = connectionProperties.getProperty(CONFIG_FILE);if (configFile == null) {configFile = System.getProperty(SYS_PROP_CONFIG_FILE);}if (configFile != null && configFile.length() > 0) {if (LOG.isInfoEnabled()) {LOG.info("DruidDataSource Config File load from : " + configFile);}Properties info = loadConfig(configFile);if (info == null) {thrownew IllegalArgumentException("Cannot load remote config file from the [config.file=" + configFile+ "].");}return info;}return null;}

阅读loadPropertyFromConfigFile方法中的源码可见,加密属性文件主要从连接属性中key为config.file的属性文件位置或系统属性中key为druid.config.file映射的加密属性文件位置加载

isDecrypt方法源码

public static final String CONFIG_DECRYPT = "config.decrypt";
public static final String SYS_PROP_CONFIG_DECRYPT = "druid.config.decrypt";
public boolean isDecrypt(Properties connectionProperties, Properties configFileProperties) {String decrypterId = connectionProperties.getProperty(CONFIG_DECRYPT);if (decrypterId == null || decrypterId.length() == 0) {if (configFileProperties != null) {decrypterId = configFileProperties.getProperty(CONFIG_DECRYPT);}}if (decrypterId == null || decrypterId.length() == 0) {decrypterId = System.getProperty(SYS_PROP_CONFIG_DECRYPT);}return Boolean.valueOf(decrypterId);}

由isDecrypt方法中源码分析可见判断是否需要解密主要看连接属性或者加载的加密属性文件变量中key为config.decrypt的值是否为true;如果以上两个的值都不存在,则继续判断系统属性key为druid.config.decrypt的值是否为true

decrypt方法源码分析

public void decrypt(DruidDataSource dataSource, Properties info) {try {String encryptedPassword = null;// 若连接属性不为空,则从连接属性中获取加密密码,否则从数据源实例中获取加密密码if (info != null) {encryptedPassword = info.getProperty(DruidDataSourceFactory.PROP_PASSWORD);}if (encryptedPassword == null || encryptedPassword.length() == 0) {encryptedPassword = dataSource.getConnectProperties().getProperty(DruidDataSourceFactory.PROP_PASSWORD);}if (encryptedPassword == null || encryptedPassword.length() == 0) {encryptedPassword = dataSource.getPassword();}// 获取公钥PublicKey publicKey = getPublicKey(dataSource.getConnectProperties(), info);// 调用ConfigTools#decrypt方法获得解密后的密文String passwordPlainText = ConfigTools.decrypt(publicKey, encryptedPassword);if (info != null) {info.setProperty(DruidDataSourceFactory.PROP_PASSWORD, passwordPlainText);} else {dataSource.setPassword(passwordPlainText);}} catch (Exception e) {thrownew IllegalArgumentException("Failed to decrypt.", e);}}

getPublicKey方法源码

publicstaticfinal String CONFIG_KEY;static {CONFIG_KEY = "config.decrypt.key";
}
public static final String SYS_PROP_CONFIG_KEY = "druid.config.decrypt.key";
// 获取公钥
public PublicKey getPublicKey(Properties connectionProperties, Properties configFileProperties) {String key = null;if (configFileProperties != null) {key = configFileProperties.getProperty(CONFIG_KEY);}if (StringUtils.isEmpty(key) && connectionProperties != null) {key = connectionProperties.getProperty(CONFIG_KEY);}if (StringUtils.isEmpty(key)) {key = System.getProperty(SYS_PROP_CONFIG_KEY);}return ConfigTools.getPublicKey(key);}

首先会去从解析加密配制文件后的属性变量中获取公钥, 获取公钥的key为config.decrypt.key;若加密配制文件属性中不存在公钥,则去数据源的连接属性中获取key为config.decrypt.key对应的公钥,如果仍然没有则去系统属性变量中获取key为druid.config.decrypt.key对应的公钥。最后调用ConfigTools#getPublicKey方法根据传入的公钥返回一个PublicKey对象

3.5 DruidAbstractDataSource#setConnectionProperties方法源码

public void setConnectionProperties(String connectionProperties) {if (connectionProperties == null || connectionProperties.trim().length() == 0) {setConnectProperties(null);return;}// 多个连接属性使用分号分隔String[] entries = connectionProperties.split(";");Properties properties = new Properties();for (int i = 0; i < entries.length; i++) {String entry = entries[i];if (entry.length() > 0) {// 每个连接属性以=号分割成name和value两部分保存到properties属性中int index = entry.indexOf('=');if (index > 0) {String name = entry.substring(0, index);String value = entry.substring(index + 1);properties.setProperty(name, value);} else {// no value is empty string which is how java.util.Properties worksproperties.setProperty(entry, "");}}}// 最后通过抽象方法调用实现类DruidDatasource类的setConnectProperties方法setConnectProperties(properties);}

其他的源码这里就不继续深入分析了,druid.jar包中涉及到ConfigTools,DruidDatasource和ConfigFilter三个类的源码掌握到这里对于实现数据库连接密码的加密和解密也已经足够了
————————————————

五 SpringBoot 2.X 同时整合 druid连接池加密 + dynamic-datasource 动态数据源

官方文档: dynamic-datasource-spring-boot-starter

1. 引入依赖

		 <dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.22</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.2</version></dependency>
————————————————

2. 编写配置文件

# 数据源配置
spring:autoconfigure:# 排除 Druid 自动配置exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfiguredatasource:# 指定使用 Druid 数据源type: com.alibaba.druid.pool.DruidDataSource# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/contentdynamic:#设置默认的数据源或者数据源组,默认值即为 masterprimary: masterdatasource:# 主库数据源master:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.0.121:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=trueusername: rootpassword: root# 从库数据源slave:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.0.122:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=trueusername: rootpassword: rootdruid:# 初始连接数initialSize: 5# 最小连接池数量minIdle: 10# 最大连接池数量maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000# 配置一个连接在池中最大生存的时间,单位是毫秒maxEvictableIdleTimeMillis: 900000# 配置检测连接是否有效validationQuery: SELECT 1 FROM DUAL
————————————————

在这里插入图片描述

3、测试

进入 Druid 控制台
在这里插入图片描述

---------------------------------------------------------------------------------------------------------------------------------------------------------------

拓展: SpringBoot 对所有密码配置加密:Jasypt

背景

对于配置中的密码(DB, MQ, Redis等),甚至账号,在生产环境下存明文,不安全,不专业,不合适。

一把插着钥匙的锁,能说它是安全的吗?

操作流程
关于Jasypt实现对配置项的加密,网络上已经有很多这方面的资料,这里简要描述下步骤。

引入依赖

<dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>1.18</version>
</dependency>

生成密文
如果计算机上有项目用过Jasypt的,那么在maven的仓库目录下会有Jasypt的jar包。如果本地仓库没有,先下载jar包。在jar包所在目录下打开cmd命令行,键入

java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="root" password=you-guess algorithm=PBEWithMD5AndDES

最下面输出的

qN66aPx0SrcFulrPfmMXOw==

是密文,在接下来要放入配置文件。
修改已有的配置
在已有的明文配置文件中,修改Jasypt密码相关配置。

spring:datasource:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mp?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=falseusername: rootpassword: ENC(qN66aPx0SrcFulrPfmMXOw==)# 配置日志
logging:level:root: infocom.heartsuit.dao: tracepattern:console: '%p%m%n'# 加密密钥
jasypt:encryptor:password: you-guess

上面的修改主要有:
在这里插入图片描述

Note: 生成的密文要用ENC()包起来,这是Jasypt的要求。

测试修改后的配置 按照上述配置,应一切正常~~

Note: 需要注意的是:

用于生成加密后的密码的密钥不可放在配置文件或者代码中,加密密钥jasypt.encryptor.password=you-guess可以对密文解密。因此,上述配置若在团队内可见,没什么影响,但是如果配置文件万一被放到了公网上,相当于把钥匙插在锁上,白加密了。。在生产环境下,建议去掉加密密钥配置:

spring:datasource:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mp?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=falseusername: rootpassword: ENC(qN66aPx0SrcFulrPfmMXOw==)# 配置日志
logging:level:root: infocom.heartsuit.dao: tracepattern:console: '%p%m%n'

仅去掉了:jasypt.encryptor.password=you-guess, 然后 jasypt.encryptor.password=you-guess可以换成另外两种方式传入:

  1. 通过服务启动参数
    java -jar xxx.jar --jasypt.encryptor.password=you-guess
    
  2. 通过系统环境变量
    这个可以通过Idea IDE传入(开发环境),或者实际的系统环境变量传入(生产环境)。

Jasypt的加密与解密

通过Jasypt命令行的方式生产密文密码后,可以用Jasypt提供的API进行解密,当然,也可以用API的方式来完成加密。

加密与解密

@Component
public class StringEncryptDecrypt {@AutowiredStringEncryptor encryptor;public String encrypt(String plainText) {// EncryptString encrypted = encryptor.encrypt(plainText);System.out.println("Encrypted: " + encrypted);return encrypted;}public String decrypt(String encrypted) {// DecryptString decrypted = encryptor.decrypt(encrypted);System.out.println("Decrypted: " + decrypted);return decrypted;}
}

总结

实现对配置文件敏感数据的加密,网上资源很多,但一定要注意安全性,不可以把公钥公开配置在文件中;还是开头那句话:

一把插着钥匙的锁,能说它是安全的吗?

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

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

相关文章

【企业转型】以企业架构为中心的SABOE数字化转型五环法

01 传统企业数字化转型面临诸多挑战 即将过去的2023年&#xff0c;chatGPT大模型、数据资产入表等事件的发生&#xff0c;标志着数字经济正在加速发展。数字经济是人类社会继农业经济、工业经济之后的第三种经济形态&#xff0c;将推动生产方式、生活方式和治理方式深刻变革&a…

Java研学-JavaScript 进阶

一 JS 的 DOM 1 概述 DOM 是 Document Object Model 文档对象模型的缩写。根据 W3C 的 DOM 规范&#xff0c;它是一种与浏览器&#xff0c;平台&#xff0c;语言无关的接口&#xff0c;能够动态地修改 XML 和 HTML。   D&#xff1a;文档 – HTML文档 或 XML 文档   O&…

CSS学习笔记整理

CSS 即 层叠样式表/CSS样式表/级联样式表&#xff0c;也是标记语言&#xff0c; 用于设置HTML页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、边框样式、边距&#xff09;以及版面的布局和外观显示样式 目录 准备工作 Chrome调…

基于FPGA的温度控制系统设计(论文+源码)

1.系统设计 本次基于FPGA的智能温度控制系统&#xff0c;以FPGA为控制核心&#xff0c;采用自顶向下的设计方法&#xff0c;按照模块化设计的思路分别实现各个模块&#xff0c;再加以整合实现整个系统&#xff0c;从而达到了温度控制的目的。系统以水箱为被控对象&#xff0c;…

环境搭建及源码运行_java环境搭建_redis安装

书到用时方恨少、觉知此时要躬行&#xff1b;拥有技术&#xff0c;成就未来&#xff0c;抖音视频教学地址&#xff1a;​​​​​​​ ​​​​​​​​​​​​​​ 1、redis 介绍 后面会有精讲redis部分&#xff0c;此部分内容着重在redis环境的搭建 Redis&#xff08;Remot…

如何让.NET应用使用更大的内存

我一直在思考为何Redis这种应用就能独占那么大的内存空间而我开发的应用为何只有4GB大小左右&#xff0c;在此基础上也问了一些大佬&#xff0c;最终还是验证下自己的猜测。 操作系统限制 主要为32位操作系统和64位操作系统。 每个进程自身还分为了用户进程空间和内核进程空…

怎么选择合适的3ds Max云渲染农场?

3ds Max 用户日常面临的一个共同挑战便是漫长的渲染周期。作为一个强大的三维建模和渲染软件&#xff0c;3ds Max 势必需处理大量的光照、材质和阴影计算任务&#xff0c;因此&#xff0c;良好的渲染方案对从业者而言尤为重口。 一、为何考虑3ds Max云渲染? 云渲染成为了解决…

重新认识Word——尾注

重新认识Word——尾注 参考文献格式文献自动生成器插入尾注将数字带上方括号将参考文献中的标号改为非上标 多处引用一篇文献多篇文献被一处引用插入尾注有横线怎么删除&#xff1f;删除尾注 前面我们学习了如何给图片&#xff0c;公式自动添加编号&#xff0c;今天我们来看看毕…

地球坐标系介绍--地心大地、地心地固直角、协议地球 坐标系

在研究地面点间几何关系的问题中&#xff0c;一般使用地球坐标系。地球坐标系固定在地球上&#xff0c;它跟随地球自转而不断地运动&#xff0c;相对地球却不存在运动&#xff0c;因此&#xff0c;地球坐标系也称地固坐标系。所有和地球保持固定关系的点&#xff0c;如地面点&a…

配电室综合监测系统

配电室综合监测系统是一种集成了自动化、智能化等技术手段的电力监控系统。它通过对配电室内的电力设备进行实时监控、数据分析和处理&#xff0c;能够提高电力设备的安全性和效率&#xff0c;及时发现并解决电力故障和潜在问题&#xff0c;保证电力系统的稳定运行。 该系统通常…

2023建筑行业薪资趋势?如何提高建筑设计效率呢?

12月6日&#xff0c;国外著名建筑可视化网站CGarchitect公布了其2023年建筑可视化薪资调查结果&#xff0c;详细描述了行业内的薪资趋势。 调查表明&#xff0c;占比较高的是有16.04%的年收入低于10000美元&#xff08;约71000人民币&#xff09;&#xff0c;其次是11.75%的受…

垂类大模型 研发方向与具体方案调研

垂类大模型 研发方向与具体方案调研 文章目录 垂类大模型 研发方向与具体方案调研一、研发方向调研初步汇总二、垂类大模型研发背景与策略选择1、垂类大模型研发背景2、垂类大模型研发策略选择&#xff08;1&#xff09;重新训练&#xff1a;&#xff08;2&#xff09;二次预训…