本章将对SpringBoot配置文件中的数据加密做自定义开发。在SpringBoot开发过程中配置文件是明文存放在application。yml或者application。properties文件中,这种配置方式会带来一定的安全隐患,本章将对这个问题提出一个简单的解决方案。编码 首先需要确定一个加密解密方式,本文采用RSA进行加密解密,首先编写加密解密的代码,注意RSA加密解密需要使用到公钥和私钥,公钥私钥的生成代码如下:publicstaticvoidgenerateKey()throwsNoSuchAlgorithmException{KeyPairGeneratorkeyPairGenKeyPairGenerator。getInstance(EncryptionType。RSA);keyPairGen。initialize(1024,newSecureRandom());KeyPairkeyPairkeyPairGen。generateKeyPair();RSAPrivateKeyprivateKey(RSAPrivateKey)keyPair。getPrivate();得到私钥RSAPublicKeypublicKey(RSAPublicKey)keyPair。getPublic();得到公钥StringpublicKeyStringnewString(Base64。encodeBase64(publicKey。getEncoded()));StringprivateKeyStringnewString(Base64。encodeBase64((privateKey。getEncoded())));System。out。println(当前生成的公钥publicKeyString);System。out。println(当前生成的私钥privateKeyString);} 加密代码如下:publicstaticStringencrypt(Stringstr,StringpublicKey)throwsNoSuchAlgorithmException,BadPaddingException,IllegalBlockSizeException,NoSuchPaddingException,InvalidKeyException,InvalidKeySpecException{byte〔〕decodedBase64。decodeBase64(publicKey);RSAPublicKeypubKey(RSAPublicKey)KeyFactory。getInstance(EncryptionType。RSA)。generatePublic(newX509EncodedKeySpec(decoded));CiphercipherCipher。getInstance(EncryptionType。RSA);cipher。init(Cipher。ENCRYPTMODE,pubKey);returnBase64。encodeBase64String(cipher。doFinal(str。getBytes(StandardCharsets。UTF8)));} 解密代码如下:publicstaticStringdecrypt(Stringstr,StringprivateKey)throwsNoSuchAlgorithmException,InvalidKeySpecException,NoSuchPaddingException,BadPaddingException,IllegalBlockSizeException,InvalidKeyException{byte〔〕inputByteBase64。decodeBase64(str。getBytes(StandardCharsets。UTF8));byte〔〕decodedBase64。decodeBase64(privateKey);RSAPrivateKeypriKey(RSAPrivateKey)KeyFactory。getInstance(EncryptionType。RSA)。generatePrivate(newPKCS8EncodedKeySpec(decoded));CiphercipherCipher。getInstance(EncryptionType。RSA);cipher。init(Cipher。DECRYPTMODE,priKey);returnnewString(cipher。doFinal(inputByte));} 上述代码为基本的加密解密工具,加下来需要在配置文件中确定哪些配置是需要进行解密的,本例将采用自定义前缀后缀的方式进行匹配,前缀为PWD〔,后缀为〕,如果在配置文件中属性值是以前缀后缀包裹的那么这个数据会被进行解密操作, 通过前文的加密工具可以先进行一次密码加密加密原文为1234qwer,公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCBOkkkvjbOQ6UTCo8U4bRCEcEtxz8haHg6lueM3NBbH3eIT7kfwQFOqj1h1qPGcQNeyn4vxzMWBAKzSQehjqVBL78GN7EZ7TEaUuWO8qsuZnOdrztX7bNKACnksSelmtbrbnFKUMAq2c2mS0o1V6iwyRxJYLGaHGXnz4KSkwIDAQAB 私钥:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIE6SSSNs5DpRMKjxThtEL8RwS3HPyFoeDqW54zc0Fsfd4hPuRBAU6qPWHWo8ZxA17KfiHMxYEArNJB6GOpUEvvwY3sRntMRpS5Y77yqy5mc52vO1fts0oAKeSz5J6Wa1utucUpQwCrZzaZLSjVXqLDJHElgsZocZefPgpKTAgMBAAECgYAFaCDjTqoQWzgu4cQ2xXK7Ur7N7bNixVyOgnu0MxDsnxZrN5qxP2wElI7Y5xgXF2diseoxqY3zn9tVEPsmwUcY73naoosx9V8oExgTBUkZYIzj1ei08zOr984zl3dbFcxOCRvqywXj9FAAGx1mhmCzFCIauJg3aX0S9mt5CwQJBAMYZsmMQ9owoXZuSclKVRfMHFpAPhQlcBM4xadhX0IRYATgNTxpESmcCoGWvyw3bvieNJyC9Njx6X4FJ2EZUzhECQQCm2IM5MlsCwyKtME5RPFna2hSqYU80UzkNfDIyMokcU2JUI4Fhigog4ol0GFMiMBsHIjScJiAwNbIsq5rsJjAkA94yVBobkETFACHBwvBIdXxy0bUF3lcKPnrrQ8bCKuVbf7xNyjfhYoXDzxNmQuMeNH6HLrpDVD3qLCGuxyuhAkAiLPl8gJWnhw9qbkdXuB0rVS1WZy9JgkblpHc5gjt9zTo0CDGaDhAftnSuMYiAefwZTSmoj85k3ExdtZJAkEArJuGNWY9HP4p7jtZX9rMokyB3517v7HQdJKBDIlOzseRCroCvU8LQURDFBUqXCRgedxgW0ZmKFf4xeawqw 加密结果为:PWD〔bMw8oqCma31JqF0DCuf5QWqSFRMigYw3fMBIIIfJ85vnmNnFbH9IcJfUHgbSmNHeITffToODwAygy4vKdzu6o1i1UQOd8w4nPKhnVJCLKqW5jmc3YwFkTIRBp63NJWzECVnRHqEKbTxPMa1gfKql2U45XxqeDSZOEXGeAE〕 得到这个数据后将其配置在application。properties文件中,具体内容如下server。port8080spring。application。nametestappspring。datasource。passwordPWD〔bMw8oqCma31JqF0DCuf5QWqSFRMigYw3fMBIIIfJ85vnmNnFbH9IcJfUHgbSmNHeITffToODwAygy4vKdzu6o1i1UQOd8w4nPKhnVJCLKqW5jmc3YwFkTIRBp63NJWzECVnRHqEKbTxPMa1gfKql2U45XxqeDSZOEXGeAE〕 接下来需要进一步解决的是如何将这个加密字符串进行解密,这里需要使用BeanFactoryPostProcessor接口对环境变量进行修改,具体实现代码如下:OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{MutablePropertySourcespropertySourcesenvironment。getPropertySources();for(PropertySourcelt;?propertySource:propertySources){if(propertySourceinstanceofOriginTrackedMapPropertySource){OriginTrackedMapPropertySourceom(OriginTrackedMapPropertySource)propertySource;MapString,Objectsourceom。getSource();source。forEach((k,v){Stringpropertyenvironment。getProperty(k);if(hasPreAndSuf(property)){LOG。info(开始处理k〔{}〕,k);try{StringrelaysplitPreAndSuf(property,this。prefix,this。suffix);StringdecryptRSAEncrypt。decrypt(relay,getPrivateKey(environment));source。put(k,decrypt);}catch(Exceptione){LOG。error(e,e);}}});}}} 处理逻辑如下:提取环境配置中的所有配置属性判断配置属性是否是OriginTrackedMapPropertySource类型,该类型的数据是在application。yaml中的内容处理OriginTrackedMapPropertySource对象的value值,如果value包含自定义前缀后缀则进行解密 在本例中对于公钥私钥以及前缀后缀是允许自定义的,开发者只需要在配置文件中根据下面表格进行填写即可 属性名称 属性含义 默认值 encryption。prefix 前缀 PWD〔 encryption。suffix 后缀 〕 encryption。rsa。publicKey 公钥 encryption。rsa。privateKey 私钥 注意:为了便捷操作开放了配置文件形式的公钥秘钥的配置,这部分配置可以在测试环境中开发环境中进行使用。如果需要在生产环境中使用请使用下面两种方式: 方式一:在项目资源目录resources文件夹下创建hfprivatekey文件,向文件中填写如下内容encryption。rsa。privateKeyMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIE6SSSNs5DpRMKjxThtEL8RwS3HPyFoeDqW54zc0Fsfd4hPuRBAU6qPWHWo8ZxA17KfiHMxYEArNJB6GOpUEvvwY3sRntMRpS5Y77yqy5mc52vO1fts0oAKeSz5J6Wa1utucUpQwCrZzaZLSjVXqLDJHElgsZocZefPgpKTAgMBAAECgYAFaCDjTqoQWzgu4cQ2xXK7Ur7N7bNixVyOgnu0MxDsnxZrN5qxP2wElI7Y5xgXF2diseoxqY3zn9tVEPsmwUcY73naoosx9V8oExgTBUkZYIzj1ei08zOr984zl3dbFcxOCRvqywXj9FAAGx1mhmCzFCIauJg3aX0S9mt5CwQJBAMYZsmMQ9owoXZuSclKVRfMHFpAPhQlcBM4xadhX0IRYATgNTxpESmcCoGWvyw3bvieNJyC9Njx6X4FJ2EZUzhECQQCm2IM5MlsCwyKtME5RPFna2hSqYU80UzkNfDIyMokcU2JUI4Fhigog4ol0GFMiMBsHIjScJiAwNbIsq5rsJjAkA94yVBobkETFACHBwvBIdXxy0bUF3lcKPnrrQ8bCKuVbf7xNyjfhYoXDzxNmQuMeNH6HLrpDVD3qLCGuxyuhAkAiLPl8gJWnhw9qbkdXuB0rVS1WZy9JgkblpHc5gjt9zTo0CDGaDhAftnSuMYiAefwZTSmoj85k3ExdtZJAkEArJuGNWY9HP4p7jtZX9rMokyB3517v7HQdJKBDIlOzseRCroCvU8LQURDFBUqXCRgedxgW0ZmKFf4xeawqw 需要将等于号后面的内容进行修改,替换为项目中的秘钥 方式二:通过命令行进行传递 在启动命令中添加Dencryption。rsa。privateKeyMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIE6SSSNs5DpRMKjxThtEL8RwS3HPyFoeDqW54zc0Fsfd4hPuRBAU6qPWHWo8ZxA17KfiHMxYEArNJB6GOpUEvvwY3sRntMRpS5Y77yqy5mc52vO1fts0oAKeSz5J6Wa1utucUpQwCrZzaZLSjVXqLDJHElgsZocZefPgpKTAgMBAAECgYAFaCDjTqoQWzgu4cQ2xXK7Ur7N7bNixVyOgnu0MxDsnxZrN5qxP2wElI7Y5xgXF2diseoxqY3zn9tVEPsmwUcY73naoosx9V8oExgTBUkZYIzj1ei08zOr984zl3dbFcxOCRvqywXj9FAAGx1mhmCzFCIauJg3aX0S9mt5CwQJBAMYZsmMQ9owoXZuSclKVRfMHFpAPhQlcBM4xadhX0IRYATgNTxpESmcCoGWvyw3bvieNJyC9Njx6X4FJ2EZUzhECQQCm2IM5MlsCwyKtME5RPFna2hSqYU80UzkNfDIyMokcU2JUI4Fhigog4ol0GFMiMBsHIjScJiAwNbIsq5rsJjAkA94yVBobkETFACHBwvBIdXxy0bUF3lcKPnrrQ8bCKuVbf7xNyjfhYoXDzxNmQuMeNH6HLrpDVD3qLCGuxyuhAkAiLPl8gJWnhw9qbkdXuB0rVS1WZy9JgkblpHc5gjt9zTo0CDGaDhAftnSuMYiAefwZTSmoj85k3ExdtZJAkEArJuGNWY9HP4p7jtZX9rMokyB3517v7HQdJKBDIlOzseRCroCvU8LQURDFBUqXCRgedxgW0ZmKFf4xeawqw 需要将等号后面的内容进行修改,替换为项目中的秘钥 从笔者所经历的角度来看命令行传递参数可能更加安全一些,因为文件和命令行相比命令行需要在生产环境才可以看到,而文件在仓库中会存在。 接下来做一个测试,在启动时输出spring。datasource。password属性查看是否是加密前的数据,测试代码如下:SpringBootApplicationpublicclassApp{Value({spring。datasource。password})privateStringdataSourceProperties;publicstaticvoidmain(String〔〕args){SpringApplication。run(App。class,args);}BeanpublicApplicationRunnerrunner(){returnargs{System。out。println(dataSourceProperties);};}} 启动项目后可以看到控制台输出1234qwer