Spring Boot 1.5 自定义 YAML 解析器

本文将介绍怎样自定义 Spring Boot 1.5 中的 YAML 解析器,通过实现 PropertySourceLoader 接口实现自定义。

通过实现 PropertySourceLoader 接口,重写 Spring Boot 的 YAML 解析器 YamlPropertySourceLoader。步骤如下:

(1)自定义实现 PropertySourceLoader 接口

(2)配置解析器

自定义实现 PropertySourceLoader 接口

我们的重点是对解析后的配置中加密字段配置进行解密,再设置回去。代码如下:

package com.hxstrive.demo.ext.YamlPropertySourceLoaderExt;

import org.springframework.beans.factory.config.YamlProcessor;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.boot.yaml.SpringProfileDocumentMatcher;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.ClassUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;

/**
 * 对 Spring Boot 默认的 Yaml 解析器进行重写
 * 对加密的配置项进行解密操作
 *
 * @author hxstrive.com
 */
public class YamlPropertySourceLoaderExt implements PropertySourceLoader {
    /** DES 加密解密的密钥KEY */
    private static final String DES_KEY_STR = "rVoO24XK^$s&VC^Gth2Dh9yWadIvsTofa*iJ4OzF_w62pDUNVt2w76u_RcGi7Vqp";

    public YamlPropertySourceLoaderExt() {}

    @Override
    public String[] getFileExtensions() {
        return new String[]{"yml", "yaml"};
    }

    @Override
    public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
        if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", (ClassLoader)null)) {
            YamlPropertySourceLoaderExt.Processor processor = new YamlPropertySourceLoaderExt.Processor(resource, profile);
            Map<String, Object> source = processor.process();
            if (!source.isEmpty()) {
                return new MapPropertySource(name, source);
            }
        }
        return null;
    }


    private static class Processor extends YamlProcessor {
        Processor(Resource resource, String profile) {
            if (profile == null) {
                this.setMatchDefault(true);
                this.setDocumentMatchers(new DocumentMatcher[]{new SpringProfileDocumentMatcher()});
            } else {
                this.setMatchDefault(false);
                this.setDocumentMatchers(new DocumentMatcher[]{new SpringProfileDocumentMatcher(new String[]{profile})});
            }

            this.setResources(new Resource[]{resource});
        }

        @Override
        protected Yaml createYaml() {
            return new Yaml(new StrictMapAppenderConstructor(), new Representer(), new DumperOptions(),
                    new Resolver() {

                @Override
                public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
                    if (tag != Tag.TIMESTAMP) {
                        super.addImplicitResolver(tag, regexp, first);
                    }
                }

            });
        }

        public Map<String, Object> process() {
            final Map<String, Object> result = new LinkedHashMap();
            this.process(new MatchCallback() {

                @Override
                public void process(Properties properties, Map<String, Object> map) {
                    // 关键点,遍历解析出来的 Map,对加密配置解密
                    decrypt(map);
                    result.putAll(Processor.this.getFlattenedMap(map));
                }

            });

            return result;
        }

        private void decrypt(Map<String, Object> map) {
            if(null == map) {
                return;
            }

            for(Map.Entry<String,Object> entry : map.entrySet()) {
                Object value = entry.getValue();
                if(value instanceof Map) {
                    // 递归调用
                    decrypt((Map<String,Object>) value);
                } else if(value instanceof String) {
                    // 开始解密
                    String valueStr = (String) value;
                    // 解密配置格式为 ENC(密文)
                    if(valueStr.matches("^ENC\\(.+\\)$")) {
                        DESUtils desUtils = new DESUtils();
                        desUtils.setKey(DES_KEY_STR);
                        // DES 解密操作
                        String val = desUtils.getDecrypt(valueStr.replaceAll("ENC\\s*\\(\\s*", "")
                                .replaceAll("\\s*\\)", ""));
                        map.put(entry.getKey(), val);
                    }
                }
            }
        }
    }

}

配置解析器

在 Spring Boot 项目的 resouces 目录下面创建 META-INF/spring.factories 文件,内容如下:

org.springframework.boot.env.PropertySourceLoader=\
  com.hxstrive.demo.ext.YamlPropertySourceLoaderExt
游手好闲地学习,并不比学习游手好闲好。 —— 约翰·贝勒斯
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号