最近在作項目的過程當中,PSS提出配置文件中相似數據庫鏈接須要的用戶名、密碼等敏感信息須要加密處理(以前一直是明文的)。git
爲了快速完成任務,網上搜刮到jasypt包,也有相應的starter,使用方法能夠參考bloggithub
可是仍是想具體弄清楚背後的實現。偶然看到Spring Boot中有個EnvironmentPostProcessor接口。看名字,它的實現類應該在配置文件加載完和Spring容器開始初始化以前起做用。這樣的話,咱們就能夠實現該接口用來定製化配置信息,包括解密。spring
話很少說,show code,數據庫
1 @Component 2 public class DecryptAESConfigProcessor implements EnvironmentPostProcessor { 3 4 private static short INDEX = 0; 5 private static String ITEM_FORMAT = "spring.config.decrypt-items[%d]"; 6 private static Pattern PATTERN = Pattern.compile("AES\\((.+)\\)"); 7 private static StringBuffer SB = new StringBuffer(); 8 9 @Override 10 public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { 11 MutablePropertySources propertySources = environment.getPropertySources(); 12 for (PropertySource propertySource: propertySources) { 13 if (propertySource instanceof OriginTrackedMapPropertySource){ 14 INDEX = 0; 15 OriginTrackedMapPropertySource otmps = (OriginTrackedMapPropertySource)propertySource; 16 //System.out.println("property name = " + otmps.getName()); 17 Map<String, Object> source = otmps.getSource(); 18 String secretSalt = source.getOrDefault("spring.config.secret-salt", "").toString(); 19 if (!"".equals(secretSalt)){ 20 String salt = CommonUtil.decrypt(secretSalt, "sns"); 21 while (INDEX > -1){ 22 String item = String.format(ITEM_FORMAT, INDEX); 23 if (source.containsKey(item)){ 24 String itemValue = source.get(item).toString(); 25 String propertyValue = source.getOrDefault(itemValue, "").toString(); 26 Matcher matcher = PATTERN.matcher(propertyValue); 27 boolean findAES = false; 28 while (matcher.find()){ 29 //decrypt each AES() 30 findAES = true; 31 String decryptStr = CommonUtil.decrypt(matcher.group(1), salt); 32 matcher.appendReplacement(SB, decryptStr); 33 } 34 if (!findAES){ 35 //decrypt entire item 36 source.put(itemValue, CommonUtil.decrypt(propertyValue, salt)); 37 } else { 38 matcher.appendTail(SB); 39 source.put(itemValue, SB.toString()); 40 } 41 SB.delete(0, SB.length()); 42 INDEX++; 43 } else { 44 INDEX = -1; 45 } 46 } 47 } 48 } 49 } 50 51 } 52 }
注意:須要將DecryptAESConfigProcessor聲明到spring.factories中。
org.springframework.boot.env.EnvironmentPostProcessor=\
org.chris.springboot.config_encrypt.config.DecryptAESConfigProcessor
public class CommonUtil { private static final String KEY_ALGORITHM = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding"; private static Cipher CIPHER; private static KeyGenerator KEY_GENERATOR; static { try { CIPHER = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); KEY_GENERATOR = KeyGenerator.getInstance(KEY_ALGORITHM); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } } /** * Decrypt by AES * @param content * @param salt * @return */ public static String decrypt(String content, String salt) { if (Objects.nonNull(content)) { try { byte[] decrypted = Base64.getDecoder().decode(content.getBytes("UTF-8")); CIPHER.init(Cipher.DECRYPT_MODE, getSecretKey(salt)); return new String(CIPHER.doFinal(decrypted)); } catch (Exception e) { e.printStackTrace(); } } return null; } /** * Encrypt by AES * @param content * @param salt * @return */ public static String encrypt(String content, String salt) { if (Objects.nonNull(content)) { try { CIPHER.init(Cipher.ENCRYPT_MODE, getSecretKey(salt)); byte[] encrypted = CIPHER.doFinal(content.getBytes("UTF-8")); return Base64.getEncoder().encodeToString(encrypted); } catch (Exception e) { e.printStackTrace(); } } return null; } /** * Generate encrypted salt * @param salt * @return */ private static SecretKeySpec getSecretKey(final String salt) { KEY_GENERATOR.init(128, new SecureRandom(salt.getBytes())); SecretKey secretKey = KEY_GENERATOR.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM); } }
最後,配置文件中須要有相似如下配置項springboot
解密規則以下,app
1. 採用AES加密解密;dom
2. 在配置文件中ide
(1) 經過spring.config.decrypt-items指定須要解密的配置spring-boot
(2) 經過spring.config.secret-salt指定AES的key(最好加密)post
3. 若是須要解密的配置項中存在AES()模式的字符串,將會解密 () 中的內容,不然解密整個配置項