配置文件隐私信息加密

配置文件隐私信息加密

项目需求

在配置SpringBoot项目时,经常需要配置数据库连接等,难免需要在配置文件里输入不可公开的密码。既然明文显示密码会导致密码泄露,而这些密码又不得不在配置文件里配置,怎么办呢?很自然可以想到,要是能在配置文件里配置加密后的密文,而配置文件在解析时动态解密就好了。

功能实现

jasypt-spring-boot这个组件为我们提供了配置属性加密的支持。本文先介绍如何使用该组件,最后展示其工作原理。

  1. 项目中导入依赖
<dependency>
	<groupId>com.github.ulisesbocchio</groupId>
	<artifactId>jasypt-spring-boot-starter</artifactId>
	<version>3.0.5</version>
</dependency>
  1. 指定加密盐值并获取密文

和其他加密工具一样,用户可以指定一串字符串作为“盐值”,用于个性化加密解密,且保护整个过程。

指定该盐值后,通过运行加密方法,可以直接获得加密后的密文。

这里通过编写请求API完成功能:

@Autowired
private StringEncryptor stringEncryptor;
@GetMapping("/test")
public void encrypt() {
    String password = "123456";
    System.out.println("加密后的密文为:");
    System.out.println(stringEncryptor.encrypt(password));
}

发送请求后控制台输出如下:

Untitled

  1. 配置文件中替换明文

如下方式可以将配置文件中的明文替换为该密文:

spring:
  datasource:
    password: ENC(3WV2dH3igOHqkq5x9S0A7Zrj2TR6tBHdi7Xe/PQlA1CwcRmizJPgSFvrgYcgVZEt)

将密文以ENC()包围,即表示括号里的内容需要先被解密,才可绑定到对应属性。用户也可以指定自己喜欢的前缀后缀来包围密文,但个人感觉ENC()的风格已经很明显,不太必要指定其他前后缀。

  1. 启动项目

启动项目时,jasypt将密文解密为原本的字符串。解密密文需要使用之前提到的盐值,该盐值同样可以在配置文件中指定:

jasypt:
  encryptor:
    password: 123456

倘若如此指定,该解密密文又被泄露,同样其他可被该密文解密的其他信息也被泄露。

那就把这个密码也加密吧!喂喂喂,这个盐值是用来解密的,可不能自己加密自己又用自己解密自己。

幸好,SpringBoot提供了多种导入配置属性的功能,可以外部导入、命令行参数导入等。如果在启动命令中以命令行参数形式指定该盐值,其他非系统成员便不可能获得该盐值的明文,具备安全性。

例如,盐值为“123456”,则可设置SpringBoot项目的启动命令为

java -jar -jasypt.encryptor.password=123456 backend.jar

jasypt解密原理

在Spring容器启动后,jasypt会遍历所有配置文件,找到指定前后缀,默认为ENC(),然后提取出其中的密文,调用解密方法得到原字符串并返回。

整个流程涉及两个接口:EncryptablePropertyDetector和EncryptablePropertyResolver。从名字可以看出,前者负责找到所有ENC(),后者解密其中的密文。

源码如下:

/**
 * Default property detector that detects encrypted property values with the format "$prefix$encrypted_value$suffix"
 * Default values are "ENC(" and ")" respectively.
 *
 * @author Ulises Bocchio
 */
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约定规则加密的属性
    @Override
    public boolean isEncrypted(String property) {
        if (property == null) {
            return false;
        }
        final String trimmedValue = property.trim();
        return (trimmedValue.startsWith(prefix) &&
                trimmedValue.endsWith(suffix));
    }

    // 去掉默认的前缀和后缀,返回加密的值
    @Override
    public String unwrapEncryptedValue(String property) {
        return property.substring(
                prefix.length(),
                (property.length() - suffix.length()));
    }
}
/**
 * @author Ulises Bocchio
 */
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);
    }
}

可以看到,jasypt提供了两个默认的实现类供使用,这也就意味着用户可以用自己的实现类替换两者,实现自己的加密解密功能。

参考博客

https://blog.csdn.net/qq_32238611/article/details/122724605

贡献者:李治圻-后端

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值