1

Java 中如何加密配置文件中的数据库账号和密码

 2 years ago
source link: http://www.justdojava.com/2022/02/26/Jasypt/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java 中如何加密配置文件中的数据库账号和密码

发表于 2022-02-26

| 分类于 Java

作为程序员每天的开发工作都离不开跟数据库打交道,而且我们的应用程序往往都会配置数据库的链接,那你有没有想过,任何一个能接触到我们项目代码的人员,都可以看到配置文件里面的账号秘密?

相信很多人的项目里面配置文件都是类似这样写的

############### Mysql配置 #########################
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/abc?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

spring.datasource.url 中配置了数据库的链接地址和端口,spring.datasource.username 配置了账号以及spring.datasource.password 中配置了密码。

如果是这样的写法,无非就是在裸奔,任何一个能接触到这个文件的人,都能够用 MySQL 的客户端工具进行数据库的链接,里面的数据毫无安全可言。当然如果是本地或者测试环境都还可以,但是对于生产环境那是不能接受的,毕竟这样的安全性太差了,一不小心被删库跑路还是有可能的。

相对而言,有一些经验的数据库运维人员是不会直接提供数据库服务的 IP 地址和端口的,而是提供域名,通过在 url 地址上面配置相应的域名,然后通过解析域名让其访问数据库服务,域名地址是不对外解析的,所以生产环境的主机以及开发人员的本机,需要进行 hosts 的配置,将域名对应 IP 地址配置起来。

这种方式会比上面直接裸奔的形式好一点,外人拿到代码,没有 hosts 配置也是不能访问数据库的,难度相对来说高了一点。但是这样也有个问题,那就是开发人员还是可以通过 MySQL 的客户端进行数据的访问,如果哪天心情不好,删库跑路也不是没有可能,或者说私自泄露数据也是有机会的。

那么很多小明就问了,有没有一种方式,可以有效的控制这种情况呢?让尽量少了人接触到数据库的数据,但是同时也不能影响开发的进度,对于开发要友好。

有问题,就会有答案,这个时候我们就需要介绍一款神器了,那就是 jasyptjasypt 可以帮助我们在配置文件中配置加密后的账号和密码,然后结合秘钥,就可以完全控制数据库的安全性。下面我们就来试一下吧。

首先有一个需要连接数据库的 Spring Boot 服务,我们先看一下,在没有引入 jasypt 的时候,是如何使用的,代码如下:

############### Mysql配置 #########################
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/abc?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

#客户端等待连接池连接的最大毫秒数
spring.datasource.hikari.connection-timeout=20000
#最小空闲连接数
spring.datasource.hikari.minimum-idle=5
#最大连接池大小
spring.datasource.hikari.maximum-pool-size=20
#连接池中空闲的最长时间毫秒
spring.datasource.hikari.idle-timeout=30000
#池中连接关闭后的最长生命周期毫秒
spring.datasource.hikari.max-lifetime=1200000
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.connection-test-query=SELECT 1

UserController.java

package com.controller;

import com.mapper.UserEntity;
import com.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.List;

@RestController
@EnableAutoConfiguration
@RequestMapping("/user")
public class UserController {

  @Autowired
  private UserMapper userMapper;

  @RequestMapping("add")
  public int addUser(String name, String desc) throws Exception {
    int id, num = 0;
    UserEntity u = new UserEntity(0, name, desc, new Date());
    num = userMapper.insert(u);
    id = u.getId();
    return id;
  }

  @RequestMapping("getlist")
  public List<UserEntity> listUser() throws Exception {
    List<UserEntity> UserEntitys = userMapper.getAll();
    System.out.println(UserEntitys.toString());
    return UserEntitys;
  }
}

UserMapper.java

package com.mapper;

import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @author ziyou
 */
public interface UserMapper {
  /**
   * 查询操作示例
   *
   * @return
   */
  @Select("SELECT * FROM t_user;")
  @Results({
    @Result(property = "id", column = "Id"),
    @Result(property = "Name", column = "Name"),
    @Result(property = "Desc", column = "Desc"),
    @Result(property = "CreateTime", column = "CreateTime")
  })
  List<UserEntity> getAll();

  /**
   * 插入操作示例
   * 将自增ID绑定到实体,keyProperty是实体字段,keyColumn是数据库字段
   *
   * @param user
   * @return
   */
  @Insert("INSERT INTO t_user(`Name`,`Desc`,CreateTime) VALUES(#{Name}, #{Desc}, #{CreateTime});")
  @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "Id")
  int insert(UserEntity user);

}

另外我们有一张表,表里面的数据是这样的,接下来我们启动服务,调用接口查询表的数据,如下所示

e6c9d24egy1h1bklwwhguj20r20ag0tr.jpg

e6c9d24egy1h1bkm0hslwj20yi0kq0uu.jpg

说明在使用明文账号密码的时候,整个流程是没有问题,接下来,我们引入 jasypt ,主要分下面三个步骤

  1. 引入 pom 依赖

    <!--springboot整合jasypt-->
        <dependency>
          <groupId>com.github.ulisesbocchio</groupId>
          <artifactId>jasypt-spring-boot-starter</artifactId>
          <version>1.18</version>
        </dependency>
    
  2. 编写加密工具类

    public class JasyptUtil {
       
      public static void main(String[] args) {
        String account = "root";
        String password = "123456";
        BasicTextEncryptor encryptor = new BasicTextEncryptor();
        //秘钥
        //encryptor.setPassword(System.getProperty("jasypt.encryptor.password"));
        encryptor.setPassword("eug83f3gG");
        //密码进行加密
        String newAccount = encryptor.encrypt(account);
        String newPassword = encryptor.encrypt(password);
        System.out.println("加密后账号:" + newAccount);
        System.out.println("加密后密码:" + newPassword);
      }
    }
    

    因为我们要得到加密后的密文,所以我们先需要根据原始账号密码,以及我们指定的秘钥来生成加密后的密文,这里我们假设本地和测试环境的秘钥为eug83f3gG,通过上面的工具类,我们可以生成如下的密文

    e6c9d24egy1h1bkm6x2cjj20y20ba756.jpg

  3. 替换账号密码,我们将 application.properties 里面的账号密码用上面的密文替换,如下所示,使用 ENC() 包住密文。

############### Mysql配置 #########################
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/abc?useUnicode=true&characterEncoding=utf-8&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.username=ENC(/YnTvPH7WnnqMHu+wKeccA==)
spring.datasource.password=ENC(Xv829RzVs7pd2sv72/wsbg==)

这里说明三点

  1. 此时我们还不能正常启动服务,因为就这样的话是启动不成功了,账号和密码是错误的,我们需要将秘钥传入,让jasypt 给我们反向解析出正确的账号和密码才能进行数据库的链接;
  2. 工具类中的秘钥保持跟生产环境不一样!!!
  3. 使用 ENC() 包住密文;

接下来我们可以将秘钥通过两种形式传进程序中使用,一种是将秘钥通过系统环境变量的形式进行配置,不过不建议;第二种是通过启动参数将秘钥进行传入;这里我们使用第二种,在 SpringBoot 项目的启动参数中,我们增加这样的配置 -Djasypt.encryptor.password=eug83f3gG,然后我们再重启应用,就可以启动成功了。

启动成功了再次访问上面的接口,可以看到效果是一样的。e6c9d24egy1h1bkmbo2loj20wg0k840j.jpg

后续在生产环境中,只需要在启动参数中传入与本地和测试环境不一样的秘钥,就可以有效的防止数据库的账号密码被泄露了,就连开发人员都不知道是什么,只要配置的运维人员知道,这个安全性就高很多了,怎么样小伙伴你学会了吗?赶紧在自己的项目中用起来吧!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK