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.mzph.cn/news/230431.shtml

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

相关文章

【Proteus仿真】【Arduino单片机】电子称重秤

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使LCD1602液晶&#xff0c;矩阵按键、蜂鸣器、HX711称重模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示HX711称重模块检测重量…

人工智能与自动驾驶:智能出行时代的未来之路

一、前言 首先&#xff0c;我们先来说下什么是人工智能&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机系统能够模拟、仿真人类智能的技术和科学领域。它涉及构建智能代理&#xff0c;使其能够感知环境、理解和…

怎么检测DC-DC电源模块稳定性?电源测试系统测试有什么优势?

DC-DC电源模块稳定性测试 稳定性是衡量DC电源模块的重要指标&#xff0c;电源模块的稳定性直接影响着电源产品和设备的工作稳定性。DC-DC电源模块的稳定性&#xff0c;可以通过检测输出电压、输出电流、负载、波形、效率等参数来评估。 1. 静态测试方法 静态测试是通过直流电压…

【DataSophon】大数据服务组件之Flink升级

&#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&am…

性能测试QPS+TPS+事务基础知识分析

事务 就是用户某一步或几步操作的集合。不过&#xff0c;我们要保证它有一个完整意义。比如用户对某一个页面的一次请求&#xff0c;用户对某系统的一次登录&#xff0c;淘宝用户对商品的一次确认支付过程。这些我们都可以看作一个事务。那么如何衡量服务器对事务的处理能力。…

部署智能合约以及 javascript 调用合约函数(Web3项目二实战之三)

在上一篇 智能合约是Web3项目的核心要务(Web3项目二实战之二) ,我们已然为项目编写了智能合约,在攥写完智能合约后,该项目将完成了一大部分,剩下无非就是用户界面交互的内容。 然而,在码完了智能合约代码后,起着承前启后关键性的便是,前端界面与智能合约的交互。 智能…

ansible远程操作主机功能(1)

自动化运维&#xff08;playbook剧本yaml&#xff09; 是基于Python开发的配置管理和应用部署工具。自动化运维中&#xff0c;现在是异军突起。 Ansible能批量配置&#xff0c;部署&#xff0c;管理上千台主机&#xff0c;类似于Xshell的一键输入的工具&#xff0c;不需要每次…

【IOS开发】传感器 SensorKit

资源 官方文档 https://developer.apple.com/search/?qmotion%20graph&typeDocumentation SensorKit 使应用程序能够访问选定的原始数据或系统从传感器处理的指标。 步骤信息加速度计或旋转速率数据用户手腕上手表的配置物理环境中的环境光有关用户日常通勤或旅行的详细…

实验用python实现决策树和随机森林分类

1.实验目的 1.会用Python提供的sklearn库中的决策树算法对数据进行分类 2.会用Python提供的sklearn库中的随机森林算法对数据进行分类 3.会用Python提供的方法对数据进行预处理 2.设备与环境 使用Spyder并借助Python语言进行实现 3.实验原理 决策树( Decision Tree) 又称为…

【论文解读】Kvazaar 2.0: Fast and Efficient Open-Source HEVC Inter Encoder

时间&#xff1a;2020 级别&#xff1a;SCI 机构&#xff1a;Tampere University 摘要&#xff1a;高效视频编码(HEVC)是当前多媒体应用中经济的视频传输和存储的关键&#xff0c;但解决其固有的计算复杂性需要强大的视频编解码器实现。本文介绍了Kvazaar 2.0 HEVC编码器&…

RDD编程

目录 一、RDD编程基础 &#xff08;一&#xff09;RDD创建 &#xff08;二&#xff09;RDD操作 1、转换操作 2、行动操作 3、惰性机制 &#xff08;三&#xff09;持久化 &#xff08;四&#xff09;分区 &#xff08;五&#xff09;一个综合实例 二、键值对RDD &am…

SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接

系列文章&#xff1a; SpringBoot Vue前后端分离项目实战 || 一&#xff1a;Vue前端设计 SpringBoot Vue前后端分离项目实战 || 二&#xff1a;Spring Boot后端与数据库连接 SpringBoot Vue前后端分离项目实战 || 三&#xff1a;Spring Boot后端与Vue前端连接 SpringBoot V…

【老牌期刊】IF:12,持续飙升,同领域期刊中的“佼佼者“,国人友好!

01 期刊概况 ARTIFICIAL INTELLIGENCE REVIEW ​ 【出版社】Springer 【ISSN】0269-2821 【EISSN】1573-7462 【检索情况】SCI&EI&Scopus在检 【WOS收录年份】1988年 【出刊频率】双月刊&#xff0c;最新一期December 2023 【期刊官网】 https://link.springe…

【Linux】进程周边005之环境变量

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.环境变量是什么&#xff1…

太阳能电池效能IV测试PV检测太阳光模拟器

目录 概述 一、系统组成 产品特点&#xff1a; 技术参数 数字源表 本系统支持Keithley24xx系列源表 标准太阳能电池 低阻测试夹具 自动化测试软件 概述 太阳能光伏器件的所有性能表征手段中&#xff0c;IV特性测试无疑是最直观、最有效、最被广泛应用的一种…

【OS】操作系统总复习笔记

操作系统总复习 文章目录 操作系统总复习一、考试题型1. 论述分析题2. 计算题3. 应用题 二、操作系统引论&#xff08;第1章&#xff09;2.1 操作系统的发展过程2.2 操作系统定义2.3 操作系统的基本特性2.3.1 并发2.3.2 共享2.3.3 虚拟2.3.4 异步 2.4 OS的功能2.5 OS结构2.5 习…

Android studio 按键控制虚拟LED

一、activity_main.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.a…

《Linux C编程实战》笔记:进程操作之创建进程

进程是一个动态的实体&#xff0c;是程序的一次执行过程。进程是操作系统资源分配的基本单位。 以下是一些概念&#xff0c;我就直接抄书了 进程是操作系统的知识&#xff0c;简单理解的话&#xff0c;你写的代码运行起来算一个进程&#xff1f; 创建进程 每个进程由进程ID号…

GPT-4.5!!!

GPT-4 还没用明白&#xff0c;GPT-4.5 就要发布了。 最近&#xff0c;OpenAI 泄露了 GPT-4.5 的发布页面&#xff0c;除了进一步增强复杂推理和跨模态理解&#xff0c;GPT-4.5 增加了一个更加强大的功能——3D。 3D 功能的进一步支持&#xff0c;也就意味着多模态最后一块版图…

关于前端学习的思考-浮动元素嵌套块级元素12.18

1、块级元素嵌套浮动元素 先摆图片&#xff0c;当橘色的盒子高度减少的时候&#xff0c;NK AD TB PK NN并不会减少。如何解决呢&#xff1f; 加一个overflow&#xff1a;clip或者hidden 2、浮动元素嵌套块级元素 加一个overflow&#xff1a;clip或者hidden 综上所述&#xff0…