文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Springboot中的不使用第三方插件对敏感信息加密处理,这种方式你知道吗?

2024-11-30 09:29

关注

概述

SpringBoot配置文件中的内容通常情况下是明文显示,安全性就比较低一些。在application.properties或application.yml,比如数据库配置信息的密码,Redis配置的密码等都是通过明文配置的,为了提供系统整体的安全性,我们需要对这些敏感的信息进行加密处理,这样即便你难道了我的配置信息你也获取不到有价值的信息。

在Springboot下我们可以通过如下两种方式简单的实现敏感信息的加密处理:

  1. Jasypt这是国外的一个开源加密工具包,功能强大。
  2. 基于EnvironmentPostProcessor实现我们可以通过实现这么一个接口来实现自己的加解密处理,我们也可以通过引入spring-cloud-context包,在该包中提供了一个DecryptEnvironmentPostProcessor处理器进行对加密信息进行解密。

关于Jasypt网上介绍的很多,这里不做介绍。这里我们主要只讲Spring Cloud Context中提供的这个EnvironmentPostprocessor。

环境配置


  org.springframework.cloud
  spring-cloud-context
  3.0.5

应用配置

encrypt:
  key: 123456789 #密钥
  salt: abcdef #加密的内容使用了加盐处理
---
spring:
  cloud:
    decrypt-environment-post-processor:
      enabled: true #开启解密功能

程序代码

现在需要对custom.password这样的一个key值进行加密处理

custom:
  password:  123456

首先需要对这明文生成加密的内容,如下方式:

public static void main(String[] args) throws Exception {
  String key = "123456789" ;
  String salt = "abcdef" ;
  String text = "123123" ;
  KeyProperties keyProperties = new KeyProperties() ;
  keyProperties.setKey(key) ;
  keyProperties.setSalt(salt) ;
  String result = TextEncryptorUtils.createTextEncryptor(keyProperties, null).encrypt(text) ;
  System.out.println(result) ;
}

通过上面的代码就可以生成加密信息,接下来就是将加密后的内容配置到配置文件中

custom:
  password:  "{cipher}2a483a44681006be6f0730b1acb45325c6bd20abe37369ef4fdb52c2e194a365"

注意:这里的内容是有格式要求的必须是:{cipher}开头。这里还有一点需要注意有可能你运行上面的代码会报错,报错信息应该是 “Illegal key size” (非法的密钥大小),比如:AES加密算法在默认JDK(Sun的JDK)配置情况下支持的密钥大小是128位,一旦超过就会报错,这时候你需要安装Java加密扩展(JCE)策略文件。下面提供了3个版本的JCE策略文件。

Java 6 JCE:

https://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html

Java 7 JCE

https://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

Java 8 JCE

https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

下载对应的版本后将其解压如下位置:

JCE策略文件安装目录

以上操作完成以后在运行上面的程序就能输出结果了,然后将结果替换配置文件内容即可。

接下来测试:

@Value("${custom.password}")
public String pwd ;
  
@GetMapping("/pwd")
public String pwd() {
  return pwd ;
}

正确的输出了我们的内容


原理

关于EnvironmentPostProcessor接口编写完成以后都会进行配置,我们上面引入的context包就对DecryptEnvironmentPostProcessor进行了配置:

配置EnvironmentPostProcessor

public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt implements EnvironmentPostProcessor, Ordered {
  @Override
  public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    // 判断是否开启了功能
    if (bootstrapEnabled(environment) || useLegacyProcessing(environment) || !isEnabled(environment)) {
      return;
    }
    // 如果环境中不存在TextEncryptor那么也不会开启
    if (!ClassUtils.isPresent("org.springframework.security.crypto.encrypt.TextEncryptor", null)) {
      return;
    }
    // 获取当前环境下的所有配置属性
    MutablePropertySources propertySources = environment.getPropertySources();
    environment.getPropertySources().remove(DECRYPTED_PROPERTY_SOURCE_NAME);
    // 解密所有的配置属性,这里也就是入口了
    Map map = TextEncryptorUtils.decrypt(this, environment, propertySources);
    if (!map.isEmpty()) {
      // 将解密后的配置添加到环境中(addFirst 表示了添加到具有最高优先级的给定属性源对象)
      propertySources.addFirst(new SystemEnvironmentPropertySource(DECRYPTED_PROPERTY_SOURCE_NAME, map));
    }
  }
  protected Boolean isEnabled(ConfigurableEnvironment environment) {
    // 获取配置属性值,是否开启功能
    return environment.getProperty("spring.cloud.decrypt-environment-post-processor.enabled", Boolean.class, true);
  }
}

解密配置

public abstract class TextEncryptorUtils {
  static Map decrypt(AbstractEnvironmentDecrypt decryptor, ConfigurableEnvironment environment, MutablePropertySources propertySources) {
    // 获取加解密服务接口
    TextEncryptor encryptor = getTextEncryptor(decryptor, environment);
    return decryptor.decrypt(encryptor, propertySources);
  }


  static TextEncryptor getTextEncryptor(AbstractEnvironmentDecrypt decryptor, ConfigurableEnvironment environment) {
    Binder binder = Binder.get(environment);
    // 将属性配置以 ‘encrypt’开头的都绑定到KeyProperties对象中
    KeyProperties keyProperties = binder.bind(KeyProperties.PREFIX, KeyProperties.class).orElseGet(KeyProperties::new);
    // 检查是否配置了encrypt.key 等核心的信息
    if (TextEncryptorUtils.keysConfigured(keyProperties)) {
      decryptor.setFailOnError(keyProperties.isFailOnError());
      // 检查是否当前CLASSPATH中是否存在RsaSecretEncryptor
      if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null)) {
        RsaProperties rsaProperties = binder.bind(RsaProperties.PREFIX, RsaProperties.class).orElseGet(RsaProperties::new);
        return TextEncryptorUtils.createTextEncryptor(keyProperties, rsaProperties);
      }
      // 如果你没有使用及配置Rsa相关的,那么就会在这里创建加解密服务接口,这里跟踪了下使用的是AES加密算法
      return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
    }
    // no keys configured
    return new TextEncryptorUtils.FailsafeTextEncryptor();
  }
}

获取到了加解密服务接口以后,接下来就是对配置属性进行解密操作

public class AbstractEnvironmentDecrypt {
  public static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
  protected Map decrypt(TextEncryptor encryptor, PropertySources propertySources) {
    Map properties = merge(propertySources);
    // 解密处理
    decrypt(encryptor, properties);
    return properties;
  }
  protected void decrypt(TextEncryptor encryptor, Map properties) {
    // 开始替换所有以{cipher}开头的属性值
    properties.replaceAll((key, value) -> {
      String valueString = value.toString();
      if (!valueString.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
        return value;
      }
      // 解密数据, key 配置属性的key, valueString要解密的数据
      return decrypt(encryptor, key, valueString);
    });
  }
  protected String decrypt(TextEncryptor encryptor, String key, String original) {
    // 截取{cipher}之后的内容
    String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
    try {
       // 解密数据
      value = encryptor.decrypt(value);
      return value;
    } catch (Exception e) {
      // ...
      return "";
    }
  }
}

整个源码处理还是非常简单的。

完毕!!!

来源:Spring全家桶实战案例源码内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯