SpringBoot项目启动时对配置文件中敏感信息加密

目录

1.自定义事件监听器

2.读取配置文件

3.有序获取配置文件信息

4.对配置项加密并回写配置文件

5.将自定义事件监听器放到SpringBoot的事件监听器列表中


        对于配置文件中某些敏感信息,如:数据库连接地址、用户名、密码等,为了安全起见,在修改之后需要加密展示,可以通过自定义事件监听器的方式在项目启动时自动对配置项进行加密。

1.自定义事件监听器

        因为我们是在项目启动时,即环境准备阶段对配置信息进行加密,所以我们需要自定义一个ApplicationEnvironmentPreparedEvent事件监听器。

        由于我们是对配置文件进行加密,所以必须在配置文件被加载之后再处理该事件,所以实现Order接口,并将执行顺序放在ConfigFileApplicationListener之后执行。

@Configuration
public class AfterConfigListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

  @Override
  public void onApplicationEvent(ApplicationEnvironmentPreparedEvent  event) {
    // ApplicationEnvironmentPreparedEvent 是加载配置文件,初始化日志系统的事件
    ConfigurableEnvironment environment = event.getEnvironment();
    //获取sursen.database.encry,判断是否需要加密
    String encry = environment.getProperty("sursen.database.encry");
    if (!StringUtils.isNull(encry) && "true".equals(encry.trim())) {
      Properties pps = PropertiesUtil.getProperties();
      //获取所有配置文件名称
      Enumeration enumeration = pps.propertyNames();
      Map<String,String> encryMap = new HashMap<>();
      while (enumeration.hasMoreElements()){
        String key = (String) enumeration.nextElement();
        String value = pps.getProperty(key);
        if(key.startsWith("sursen.database") && !"sursen.database.encry".equals(key)){
          //判断字符串是否为base64,如果是则代表加密过,不再进行加密
          Boolean isBase64 = StringUtils.checkBase64(value);
          String encryValue = isBase64 ? value
              : new String(Base64.getEncoder().encode(value.getBytes()), StandardCharsets.UTF_8);
          //对密码解密后重新赋值,用以连接数据库时使用
          String decValue = new String(Base64.getDecoder().decode(encryValue.getBytes()),StandardCharsets.UTF_8);
          System.setProperty(key, decValue);
          encryMap.put(key,encryValue);
        }
      }
      //将加密后的配置信息回写到配置文件中
      PropertiesUtil.setValues(encryMap);
    }
  }

  @Override
  public int getOrder() {
    return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
  }

}

2.读取配置文件

public class PropertiesUtil {

    private static Logger logger = Logger.getLogger(PropertiesUtil.class);
     /**
     * 获取Properties对象
     * @return
     */
    public static Properties getProperties(){
        Properties properties = new LinkedProperties();
        InputStream inputStream = null;
        try {
            //application.properties在resources目录下
            inputStream = PropertiesUtil.class.getClassLoader().getResourceAsStream("application.properties");
            properties.load(inputStream);
        } catch (FileNotFoundException e) {
            logger.error("application.properties文件未找到!");
        } catch (IOException e) {
            logger.error("读取application.properties出现IOException:",e);
        } finally {
            try {
                if (null != inputStream){
                    inputStream.close();
                }
            } catch (IOException e) {
                logger.error("application.properties文件流关闭出现异常");
            }
        }
        return properties;
    }

    /**
     * 根据key查询value值
     * @param key key
     * @return
     */
    public static String getValue(String key){
        Properties properties = getProperties();
        String value = properties.getProperty(key);
        return value;
    }

    public static void setValue(String key,String value){
        Properties properties = getProperties();
        properties.setProperty(key, value);
        writeBackPropertyFile(properties);
    }
    /**
     * 新增/修改数据
     * @param map
     */
    public static void setValues(Map<String,String> map){
        Properties properties = getProperties();
        map.forEach((key,value)->{
            properties.setProperty(key, value);
        });
        writeBackPropertyFile(properties);
    }

    /**
     * 回写配置文件
     * @param properties
     * 此处获取的路径是target下classes
     * 这里的path是项目文件的绝对路径
     * 先获取项目绝对路径:Thread.currentThread().getContextClassLoader().getResource("").getPath();
     * 然后在项目路径后面拼接"properties/sysConfig.properties";
     */
    private static void writeBackPropertyFile(Properties properties){
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath()
            + "application.properties";
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(path);
            properties.store(fileOutputStream, "注释");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != fileOutputStream){
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                System.out.println("application.properties文件流关闭出现异常");
            }
        }
    }
}

3.有序获取配置文件信息

public class LinkedProperties extends Properties {

  private static final long serialVersionUID = -4627607243846121965L;

   // 因为LinkedHashSet有序,所以,key在调用put()的时候,存放到这里也就有序。
  private final LinkedHashSet<Object> keys = new LinkedHashSet<>();

  @Override
  public Enumeration<Object> keys() {
    return Collections.enumeration(keys);
  }

  /**
   * 在put的时候,只是把key有序的存到{@link LinkedProperties#keys}
   * 取值的时候,根据有序的keys,可以有序的取出所有value
   * 依然调用父类的put方法,也就是key value 键值对还是存在hashTable里.
   * 只是现在多了个存key的属性{@link LinkedProperties#keys}
   */
  @Override
  public Object put(Object key, Object value) {
    keys.add(key);
    return super.put(key, value);
  }

  @Override
  public Set<String> stringPropertyNames() {
    Set<String> set = new LinkedHashSet<>();
    for (Object key : this.keys) {
      set.add((String) key);
    }
    return set;
  }

  @Override
  public Set<Object> keySet() {
    return keys;
  }

  @Override
  public Enumeration<?> propertyNames() {
    return Collections.enumeration(keys);
  }

}

4.对配置项加密并回写配置文件

此处我使用的是base64加密,判断字符串是否为base64格式请参考博客:判断字符串是否为base64编码_敲代码的哈士奇的博客-CSDN博客_判断是否是base64

5.将自定义事件监听器放到SpringBoot的事件监听器列表中

@SpringBootApplication
public class SystemManagerApplication  extends SpringBootServletInitializer {

    //如果是以war包的方式在Tomcat中运行springboot项目,则需要继承SpringBootServletInitializer,
    //并在configure方法中将自定义事件监听器添加进去
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.listeners(new AfterConfigListener()).sources(SystemManagerApplication.class);
    }


    public static void main(String[] args){
        SpringApplication application = new SpringApplication(SystemManagerApplication.class);
        application.addListeners(new AfterConfigListener());
        application.run(args);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jasypt(Java Simplified Encryption)是一个Java加密库,它可以用来加密/解密文本、哈希密码等。Spring Boot提供了对Jasypt的支持,可以在应用程序轻松使用Jasypt来加密敏感信息。下面是使用Jasypt对MySQL配置文件进行加密的步骤: 1. 引入Jasypt依赖 在pom.xml文件添加以下依赖: ```xml <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> ``` 2. 配置加密 在application.properties或者application.yml添加以下配置: ```properties jasypt.encryptor.password=your_password ``` 其,your_password是用来加密敏感信息的密码。 3. 加密MySQL配置信息 在application.properties或者application.yml加入MySQL配置信息,如下: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=root ``` 将密码部分使用Jasypt进行加密,如下: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=ENC(加密后的密码) ``` 其加密后的密码可以使用Jasypt提供的加密工具进行加密。例如,我们可以使用以下命令生成加密后的密码: ``` java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="root" password="your_password" algorithm=PBEWithMD5AndDES ``` 其,jasypt-1.9.2.jar是Jasypt的jar包,your_password是用来加密敏感信息的密码。执行以上命令后,会输出加密后的密码。 4. 配置解密 在启动应用程序时,Spring Boot会自动解密加密敏感信息。因此,我们不需要任何额外的配置来解密MySQL密码。只需将加密后的密码放入配置文件即可。 至此,我们已经成功地使用Jasypt对MySQL配置文件进行了加密
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值