zoukankan      html  css  js  c++  java
  • Spring Boot

    最近在做项目的过程中,PSS提出配置文件中类似数据库连接需要的用户名、密码等敏感信息需要加密处理(之前一直是明文的)。

    为了快速完成任务,网上搜刮到jasypt包,也有相应的starter,使用方法可以参考blog

    但是还是想具体弄清楚背后的实现。偶然看到Spring Boot中有个EnvironmentPostProcessor接口。看名字,它的实现类应该在配置文件加载完和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);
        }
    }

    最后,配置文件中需要有类似以下配置项

    解密规则如下,

    1. 采用AES加密解密;

    2. 在配置文件中

     (1) 通过spring.config.decrypt-items指定需要解密的配置

     (2) 通过spring.config.secret-salt指定AES的key(最好加密)

    3. 如果需要解密的配置项中存在AES()模式的字符串,将会解密 () 中的内容,否则解密整个配置项

  • 相关阅读:
    磁盘读写监控
    system.stat[resource,<type>]
    zookeeper核心之基于 Curator 实现分布式锁
    zookeeper之服务端接收数据请求和集群模式下的处理流程
    zookeeper之事件触发
    zookeeper之客户端接收服务端处理完成的响应
    zookeeper之服务端接收请求处理流程
    zookeeper之Watcher的基本流程
    zookeeper原理之Leader选举完成之后的处理逻辑
    zookeeper原理之投票的网络通信流程
  • 原文地址:https://www.cnblogs.com/hello-yz/p/10938780.html
Copyright © 2011-2022 走看看