springboot配置文件加密

背景

对于springboot项目,项目中依赖的配置如:数据库密码、中间件密码等都是明文保存在配置文件中的。
这种方式存在很大的风险,在企业的安全扫描中也通过不了。为了安全的需要,所以要对配置文件进行加密处理。

使用方式

集成 jasypt

pom 引入依赖

<!-- 配置文件加密 -->
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

application.yml 中增加

# 秘钥 不支持中文
jasypt:
  encryptor:
    password: fandf

编写测试类 pom 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>
package com.fandf.demo;

import org.jasypt.encryption.StringEncryptor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

/**
 * @author fandongfeng
 */

@SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class JasyptTest {

    @Resource
    private StringEncryptor stringEncryptor;

    /**
     * example:
     *  redis:password= 123
     *  123 加密后的内容:
     * 修改后的配置文件
     *  redis:password= ENC(QvHbj+BCPR/bnSdDuNKzpoB3Kw9Pvm1bEIaSqlD5Ohhp8rPKBUcj0f5V+QqWsPP0)
     * ENC(mVTvp4IddqdaYGqPl9lCQbzM3H/b0B6l)
     */

    @Test
    public void encrypt() {
        String encrypt = stringEncryptor.encrypt("UUID");
        //UUID  加密后的内容:AkZs6tdmJojZe74vfIi5BRXbUvqkBiNQrn3RO22GeqW9sBjbNuIcNz+GdqkqJ8DQ
        System.out.println(" UUID  加密后的内容:" + encrypt);
        String decrypt = stringEncryptor.decrypt(encrypt);
        System.out.println("解密后的内容:" + decrypt);
    }

}

修改配置文件

fdf:
  trace:
    enable: true
    style: ENC(AkZs6tdmJojZe74vfIi5BRXbUvqkBiNQrn3RO22GeqW9sBjbNuIcNz+GdqkqJ8DQ)

启动项目,看下效果

springboot配置文件加密

原理

源码链接:https://github.com/ulisesbocchio/jasypt-spring-boot

查看其自动引入的类

springboot配置文件加密

查看 JasyptSpringBootAutoConfiguration 类

springboot配置文件加密

EnableEncryptablePropertiesConfiguration

@Configuration
@Import({EncryptablePropertyResolverConfiguration.class, CachingConfiguration.class})
public class EnableEncryptablePropertiesConfiguration {
    private static final Logger log = LoggerFactory.getLogger(EnableEncryptablePropertiesConfiguration.class);

    public EnableEncryptablePropertiesConfiguration() {
    }

    @Bean
    public static EnableEncryptablePropertiesBeanFactoryPostProcessor enableEncryptablePropertySourcesPostProcessor(ConfigurableEnvironment environment, EncryptablePropertySourceConverter converter) {
        return new EnableEncryptablePropertiesBeanFactoryPostProcessor(environment, converter);
    }
}

查看 EnableEncryptablePropertiesBeanFactoryPostProcessor


public class EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
    private static final Logger log = LoggerFactory.getLogger(EnableEncryptablePropertiesBeanFactoryPostProcessor.class);
    private final ConfigurableEnvironment environment;
    private final EncryptablePropertySourceConverter converter;

    public EnableEncryptablePropertiesBeanFactoryPostProcessor(ConfigurableEnvironment environment, EncryptablePropertySourceConverter converter) {
        this.environment = environment;
        this.converter = converter;
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("Post-processing PropertySource instances");
        MutablePropertySources propSources = this.environment.getPropertySources();
        this.converter.convertPropertySources(propSources);
    }

    public int getOrder() {
        return 2147483547;
    }
}

这里就基本看明白了,通过 bean 工厂后置处理器,在加载配置文件后进行操作。

对配置文件进行解密这边主要是有两个接口,分别是 EncryptablePropertyDetector、EncryptablePropertyResolver,这两个接口根据名称可以看出来一个是发现器,一个是分解器。
先来看看 EncryptablePropertyDetector

public interface EncryptablePropertyDetector {
    boolean isEncrypted(String var1);

    String unwrapEncryptedValue(String var1);
}
public class DefaultPropertyDetector implements EncryptablePropertyDetector {
    //默认前后缀,可自定义配置
    private String prefix = "ENC(";
    private String suffix = ")";

    public DefaultPropertyDetector() {
    }

    public DefaultPropertyDetector(String prefix, String suffix) {
        Assert.notNull(prefix, "Prefix can't be null");
        Assert.notNull(suffix, "Suffix can't be null");
        this.prefix = prefix;
        this.suffix = suffix;
    }

    //判断是否按照jasypt约定规则加密的属性
    public boolean isEncrypted(String property) {
        if (property == null) {
            return false;
        } else {
            String trimmedValue = property.trim();
            return trimmedValue.startsWith(this.prefix) && trimmedValue.endsWith(this.suffix);
        }
    }
    //去掉前缀和后缀,返回加密的值
    public String unwrapEncryptedValue(String property) {
        return property.substring(this.prefix.length(), property.length() - this.suffix.length());
    }
}

接着看 EncryptablePropertyResolver

public interface EncryptablePropertyResolver {
    String resolvePropertyValue(String var1);
}
//默认实现类
public class DefaultPropertyResolver implements EncryptablePropertyResolver {

    private final Environment environment;
    // 加密和解密的实现
    private StringEncryptor encryptor;
    // jasypt默认发现器
    private EncryptablePropertyDetector detector;

    public DefaultPropertyResolver(StringEncryptor encryptor, Environment environment) {
        this(encryptor, new DefaultPropertyDetector(), environment);
    }

    public DefaultPropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector, Environment environment) {
        this.environment = environment;
        Assert.notNull(encryptor, "String encryptor can't be null");
        Assert.notNull(detector, "Encryptable Property detector can't be null");
        this.encryptor = encryptor;
        this.detector = detector;
    }

    @Override
    public String resolvePropertyValue(String value) {
        // 该方法获取加密的属性,然后使用StringEncryptor解密并返回
        return Optional.ofNullable(value)
                .map(environment::resolvePlaceholders)
                .filter(detector::isEncrypted) // 过滤加密属性
                .map(resolvedValue -> {
                    try {
                        // 去除前缀和后缀获取真正加密的值
                        String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim());
                        String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty);
                        // 解密获得明文
                        return encryptor.decrypt(resolvedProperty);
                    } catch (EncryptionOperationNotPossibleException e) {
                        throw new DecryptionException("Unable to decrypt property: " + value + " resolved to: " + resolvedValue + ". Decryption of Properties failed,  make sure encryption/decryption " +
                                "passwords match", e);
                    }
                })
                .orElse(value);
    }
}

更多技术文章可以查看我的博客地址:http://fandf.top


原文始发于微信公众号(好好学技术):springboot配置文件加密

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/219680.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!