SpringBoot配置文件中的数据加密

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: SpringBoot配置文件中的数据加密

在SpringBoot开发过程中配置文件是明文存放在application.yml或者application.properties文件中,这种配置方式会带来一定的安全隐患,本章将对这个问题提出一个简单的解决方案。

1,编码

首先需要确定一个加密解密方式,本文采用RSA进行加密解密,首先编写加密解密的代码,注意RSA加密解密需要使用到公钥和私钥,公钥私钥的生成代码如下:

public static void generateKey() throws NoSuchAlgorithmException {
   KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(EncryptionType.RSA);
   keyPairGen.initialize(1024, new SecureRandom());
   KeyPair keyPair = keyPairGen.generateKeyPair();
   RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
   RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
   String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
   String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
   System.out.println("当前生成的公钥= " + publicKeyString);
   System.out.println("当前生成的私钥= " + privateKeyString);
}

加密代码如下:

public static String encrypt(String str, String publicKey) throws NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
                byte[] decoded = Base64.decodeBase64(publicKey);
                RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(EncryptionType.RSA).generatePublic(new X509EncodedKeySpec(decoded));
                Cipher cipher = Cipher.getInstance(EncryptionType.RSA);
                cipher.init(Cipher.ENCRYPT_MODE, pubKey);
                return Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        }

解密代码如下:

public static String decrypt(String str, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
   byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
   byte[] decoded = Base64.decodeBase64(privateKey);
   RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(EncryptionType.RSA).generatePrivate(new PKCS8EncodedKeySpec(decoded));
   Cipher cipher = Cipher.getInstance(EncryptionType.RSA);
   cipher.init(Cipher.DECRYPT_MODE, priKey);
   return new String(cipher.doFinal(inputByte));
}

上述代码为基本的加密解密工具,加下来需要在配置文件中确定哪些配置是需要进行解密的,本例将采用自定义前缀+后缀的方式进行匹配,前缀为PWD[,后缀为],如果在配置文件中属性值是以前缀后缀包裹的那么这个数据会被进行解密操作,

通过前文的加密工具可以先进行一次密码加密加密原文为1234qwer,公钥

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCBOkkkvjbOQ6UTCo8U4bRC/EcEtxz8haHg6lueM3NBbH3eIT7kfwQFOqj1h1qPGcQNeyn4vxzMWBAKzSQehjqVBL7/8GN7EZ7TEaUuWO+8qsuZnOdrztX7bNKACnks+SelmtbrbnFKUMAq2c2mS0o1V6iwyRxJYLGaHGXnz4KSkwIDAQAB

私钥:

MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIE6SSS+Ns5DpRMKjxThtEL8RwS3HPyFoeDqW54zc0Fsfd4hPuR/BAU6qPWHWo8ZxA17Kfi/HMxYEArNJB6GOpUEvv/wY3sRntMRpS5Y77yqy5mc52vO1fts0oAKeSz5J6Wa1utucUpQwCrZzaZLSjVXqLDJHElgsZocZefPgpKTAgMBAAECgYAFaCDjTqoQWzgu4cQ2xXK7Ur7N7bNixVyOgn+u0MxDsnxZrN5qxP2wElI7Y5xgXF2diseoxqY3zn9tVEPsmwUcY73naoosx9V8oExgT/BUkZYIzj1ei08zOr984zl3dbFcxOCRvqywXj9FAAGx1mhmCzFCIauJg3aX0S9mt5/CwQJBAMYZsmMQ9owoXZuSclKVRfMHFpAPhQlcBM4xadhX0IRYATgNTxpESmcCoGWvyw3bvieNJyC9Njx6X4FJ2EZUzhECQQCm/2IM5MlsCwyKtME5RPFna2hSqYU80UzkNfDIyMokcU2JUI4Fhigog4ol0GFMiMBsHIjS+cJiAwNbIsq5rsJjAkA94yVBobkETFACHBwvBIdXxy0bUF3lcKPnrrQ8bCKuVbf7xNyjfhYoXD+zxNmQuMeNH6HLrpDVD/3qLCGuxyuhAkAiLPl/8gJWnhw+9qbkdXuB0rVS1WZy/9JgkblpHc5gjt9zTo0CDGaDhAftnSuMYiAe/+fwZTSmoj85k3ExdtZJAkEArJuG/NWY9HP4p7jtZX9rMokyB3517v7HQdJKBDIlOzseRC/roCvU8LQ/URDFBUqXCRgedxgW+0ZmKFf4xeawqw==

加密结果为:

PWD[bMw8oqC/ma31JqF0DCuf5QWqSFRMigYw3fMBIIIfJ85vnmNnFbH9IcJfUHgbSmNHeITffToODwAygy4vKdzu6o1i1UQOd8w4nPKhnVJCLKqW5jmc3Yw+FkTIRBp63NJWzECVnRHqEK+bTxPMa1gfKql/2U45XxqeDSZOEXGeA+E=]

得到这个数据后将其配置在application.properties文件中,具体内容如下

server.port=8080
spring.application.name=test-app
spring.datasource.password=PWD[bMw8oqC/ma31JqF0DCuf5QWqSFRMigYw3fMBIIIfJ85vnmNnFbH9IcJfUHgbSmNHeITffToODwAygy4vKdzu6o1i1UQOd8w4nPKhnVJCLKqW5jmc3Yw+FkTIRBp63NJWzECVnRHqEK+bTxPMa1gfKql/2U45XxqeDSZOEXGeA+E=]

接下来需要进一步解决的是如何将这个加密字符串进行解密,这里需要使用BeanFactoryPostProcessor接口对环境变量进行修改,具体实现代码如下:

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   MutablePropertySources propertySources = environment.getPropertySources();
   for (PropertySource<?> propertySource : propertySources) {
      if (propertySource instanceof OriginTrackedMapPropertySource
      ) {
         OriginTrackedMapPropertySource om = (OriginTrackedMapPropertySource) propertySource;
         Map<String, Object> source = om.getSource();
         source.forEach((k, v) -> {
            String property = environment.getProperty(k);
            if (hasPreAndSuf(property)) {
               LOG.info("开始处理 k = [{}]", k);
               try {
                  String relay = splitPreAndSuf(property, this.prefix, this.suffix);
                  String decrypt = RSAEncrypt.decrypt(relay, getPrivateKey(environment));
                  source.put(k, decrypt);
               }
               catch (Exception e) {
                  LOG.error("e = ", e);
               }
            }
         });
      }
   }
}

2,处理逻辑

提取环境配置中的所有配置属性

判断配置属性是否是OriginTrackedMapPropertySource类型,该类型的数据是在application.yaml中的内容

处理OriginTrackedMapPropertySource对象的value值,如果value包含自定义前缀后缀则进行解密

在本例中对于公钥私钥以及前缀后缀是允许自定义的,开发者只需要在配置文件中根据下面表格进行填写即可

属性名称 属性含义 默认值
encryption.prefix 前缀 PWD[
encryption.suffix 后缀 默认值
encryption.rsa.publickey 公钥 默认值
encryption.rsa.privatekey 私钥 默认值

注意:为了便捷操作开放了配置文件形式的公钥秘钥的配置,这部分配置可以在测试环境中开发环境中进行使用。如果需要在生产环境中使用请使用下面两种方式:

方式一:在项目资源目录resources文件夹下创建hf_private_key文件,向文件中填写如下内容

encryption.rsa.privateKey=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIE6SSS+Ns5DpRMKjxThtEL8RwS3HPyFoeDqW54zc0Fsfd4hPuR/BAU6qPWHWo8ZxA17Kfi/HMxYEArNJB6GOpUEvv/wY3sRntMRpS5Y77yqy5mc52vO1fts0oAKeSz5J6Wa1utucUpQwCrZzaZLSjVXqLDJHElgsZocZefPgpKTAgMBAAECgYAFaCDjTqoQWzgu4cQ2xXK7Ur7N7bNixVyOgn+u0MxDsnxZrN5qxP2wElI7Y5xgXF2diseoxqY3zn9tVEPsmwUcY73naoosx9V8oExgT/BUkZYIzj1ei08zOr984zl3dbFcxOCRvqywXj9FAAGx1mhmCzFCIauJg3aX0S9mt5/CwQJBAMYZsmMQ9owoXZuSclKVRfMHFpAPhQlcBM4xadhX0IRYATgNTxpESmcCoGWvyw3bvieNJyC9Njx6X4FJ2EZUzhECQQCm/2IM5MlsCwyKtME5RPFna2hSqYU80UzkNfDIyMokcU2JUI4Fhigog4ol0GFMiMBsHIjS+cJiAwNbIsq5rsJjAkA94yVBobkETFACHBwvBIdXxy0bUF3lcKPnrrQ8bCKuVbf7xNyjfhYoXD+zxNmQuMeNH6HLrpDVD/3qLCGuxyuhAkAiLPl/8gJWnhw+9qbkdXuB0rVS1WZy/9JgkblpHc5gjt9zTo0CDGaDhAftnSuMYiAe/+fwZTSmoj85k3ExdtZJAkEArJuG/NWY9HP4p7jtZX9rMokyB3517v7HQdJKBDIlOzseRC/roCvU8LQ/URDFBUqXCRgedxgW+0ZmKFf4xeawqw==

需要将等于号后面的内容进行修改,替换为项目中的秘钥

方式二:通过命令行进行传递

在启动命令中添加

-Dencryption.rsa.privateKey=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIE6SSS+Ns5DpRMKjxThtEL8RwS3HPyFoeDqW54zc0Fsfd4hPuR/BAU6qPWHWo8ZxA17Kfi/HMxYEArNJB6GOpUEvv/wY3sRntMRpS5Y77yqy5mc52vO1fts0oAKeSz5J6Wa1utucUpQwCrZzaZLSjVXqLDJHElgsZocZefPgpKTAgMBAAECgYAFaCDjTqoQWzgu4cQ2xXK7Ur7N7bNixVyOgn+u0MxDsnxZrN5qxP2wElI7Y5xgXF2diseoxqY3zn9tVEPsmwUcY73naoosx9V8oExgT/BUkZYIzj1ei08zOr984zl3dbFcxOCRvqywXj9FAAGx1mhmCzFCIauJg3aX0S9mt5/CwQJBAMYZsmMQ9owoXZuSclKVRfMHFpAPhQlcBM4xadhX0IRYATgNTxpESmcCoGWvyw3bvieNJyC9Njx6X4FJ2EZUzhECQQCm/2IM5MlsCwyKtME5RPFna2hSqYU80UzkNfDIyMokcU2JUI4Fhigog4ol0GFMiMBsHIjS+cJiAwNbIsq5rsJjAkA94yVBobkETFACHBwvBIdXxy0bUF3lcKPnrrQ8bCKuVbf7xNyjfhYoXD+zxNmQuMeNH6HLrpDVD/3qLCGuxyuhAkAiLPl/8gJWnhw+9qbkdXuB0rVS1WZy/9JgkblpHc5gjt9zTo0CDGaDhAftnSuMYiAe/+fwZTSmoj85k3ExdtZJAkEArJuG/NWY9HP4p7jtZX9rMokyB3517v7HQdJKBDIlOzseRC/roCvU8LQ/URDFBUqXCRgedxgW+0ZmKFf4xeawqw==

需要将等号后面的内容进行修改,替换为项目中的秘钥

从笔者所经历的角度来看命令行传递参数可能更加安全一些,因为文件和命令行相比命令行需要在生产环境才可以看到,而文件在仓库中会存在。

接下来做一个测试,在启动时输出

spring.datasource.password属性查看是否是加密前的数据,测试代码如下:

@SpringBootApplication
public class App {
   @Value("${spring.datasource.password}")
   private String dataSourceProperties;
   public static void main(String[] args) {
      SpringApplication.run(App.class, args);
   }
   @Bean
   public ApplicationRunner runner() {
      return args -> {
         System.out.println(dataSourceProperties);
      };
   }
}

启动项目后可以看到控制台输出

1234qwer
目录
相关文章
|
2月前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
68 0
|
3月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
63 4
|
3月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
57 0
|
9天前
|
Java Maven Spring
SpringBoot配置跨模块扫描问题解决方案
在分布式项目中,使用Maven进行多模块开发时,某些模块(如xxx-common)没有启动类。如何将这些模块中的类注册为Spring管理的Bean对象?本文通过案例分析,介绍了两种解决方案:常规方案是通过`@SpringBootApplication(scanBasePackages)`指定扫描路径;推荐方案是保持各模块包结构一致(如com.xxx),利用SpringBoot默认扫描规则自动识别其他模块中的组件,简化配置。
SpringBoot配置跨模块扫描问题解决方案
|
16天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
66 14
|
2月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
56 1
SpringBoot入门(7)- 配置热部署devtools工具
|
3月前
|
存储 算法 安全
SpringBoot 接口加密解密实现
【10月更文挑战第18天】
|
2月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
55 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
2月前
|
存储 前端开发 JavaScript
springboot中路径默认配置与重定向/转发所存在的域对象
Spring Boot 提供了简便的路径默认配置和强大的重定向/转发机制,通过合理使用这些功能,可以实现灵活的请求处理和数据传递。理解并掌握不同域对象的生命周期和使用场景,是构建高效、健壮 Web 应用的关键。通过上述详细介绍和示例,相信读者能够更好地应用这些知识,优化自己的 Spring Boot 应用。
40 3
|
2月前
|
Java 数据库连接
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
69 8