# Profile_Encryption
**Repository Path**: wubindong/Profile_Encryption
## Basic Information
- **Project Name**: Profile_Encryption
- **Description**: 通过学习jasypt框架实现加密配置中数据库密码
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-10-27
- **Last Updated**: 2025-07-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 工程简介
加密配置数据库密码
# 延伸阅读
## 1. 学习资料
[数据库密码配置项都不加密?心也太大了吧!](https://www.bilibili.com/read/cv5770200)
[springboot使用jasypt对配置文件加密,加密数据库连接](https://blog.csdn.net/UnicornRe/article/details/122948098)
[Springboot项目中应用jasypt进行加解密](https://www.jianshu.com/p/611836e889ba)
## 2. 数据库设计 和配置信息
### 2.1 创建数据库和用户
```sql
create table database_dev;
create user 'student'@'%' identified by '123';
grant all privileges on database_dev.* to 'student'@'%';
```
### 2.2 创建数据表格
```sql
create table if not exists student( sname varchar(20) , sage int, sno int unsigned AUTO_INCREMENT primary key);
```
### 2.3 常规连接数据库配置

但是根据参考文献可知,这是不安全的。
> 一个程序员把自己公司的项目代码上传到了自己的GitHub仓库里了,结果配置文件忘了处理,导致公司数据库泄露,关键问题是,这个公司还是个酒店管理公司 作者:CodeSheep https://www.bilibili.com/read/cv5770200 出处:bilibili
### 2.4 需要加密信息
哪些信息要加密呢?
一般来说,项目配置文件里,所有涉及信息安全的配置项(或字段)都应该做处理,典型的比如:
1) 用到的数据库、缓存的密码
2) 用到的中间件、消息队列的密码
3) 用到的各种第三方服务的Access_Key
......等等
### 2.5 加密配置项需要依赖
```xml
com.github.ulisesbocchio
jasypt-spring-boot-starter
3.0.4
```
### 2.6 让加密更安全 (配置信息)
#### 1. 用自定义的前后缀 (本次选择)
```properties
# 配置加密密钥 (通过配置文件创建)
#jasypt.encryptor.password=CodeSheep
# 加密字段就可以放在 自定义的 prefix 和 suffix 之间
jasypt.encryptor.property.prefix=^BONC[
jasypt.encryptor.property.suffix=]
# 测试的数据库用户名&密码: student 123
spring.datasource.username=student
spring.datasource.password=^BONC[+e42ZQMaDpGJwmEnaXT//zUl/CXRE0SfMD2OKri/zckVaFBaZ3xzEFSY1Stl1XYo]
```
#### 2. 使用自定义加密器 (本次选择)
上文实验加密时,使用的是默认的加密规则,这一点会让当自定义加密密钥泄漏时可能变得不安全。为此我们可以自定义加密规则。
//配置生成密钥的代码:对应jasypt.encryptor.password
```java
package com.example.profile_encryption.config;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Formatter;
/**
* HMAC(Hash Message Authentication Code,散列消息鉴别码)
* 使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证,
*/
@Slf4j
public class ShaConfig {
private static final String HASH_ALGORITHM = "HmacSHA256";
//加密接口
public static String getAfterSha(String str,String key){
String afterShaString = "";
try {
afterShaString = hashMac(str, key);
} catch (SignatureException e) {
log.error("获取加密字符串失败,",e);
}
return afterShaString;
}
/**
* hashMac Method that encrypt the data and convert into hex values... HMAC加密
* @param text 消息
* @param secretKey 密钥
* @return 加密后字符串
* @throws SignatureException
*/
public static String hashMac(String text, String secretKey)
throws SignatureException {
try {
Key sk = new SecretKeySpec(secretKey.getBytes(), HASH_ALGORITHM);
Mac mac = Mac.getInstance(sk.getAlgorithm());
mac.init(sk); // 初始化mac
final byte[] hmac = mac.doFinal(text.getBytes());
return toHexString(hmac);
} catch (NoSuchAlgorithmException e1) {
throw new SignatureException(
"error building signature, no such algorithm in device "
+ HASH_ALGORITHM);
} catch (InvalidKeyException e) {
throw new SignatureException(
"error building signature, invalid key " + HASH_ALGORITHM);
}
}
//toHexString Method...
public static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : bytes) {
formatter.format("%02x", b);
}
return sb.toString();
}
}
```
//返回自定义加密器
```java
package com.example.profile_encryption.config;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EncryptorCfg {
//加密算法
private static final String ALGRI = "PBEWITHHMACSHA512ANDAES_256";
//哈希迭代次数
private static final String ITERATIONS = "10000";
private static final String worKey = "work_key_value";
private static final String rootKey = "root_key_value";
private String buildKey() {
return SHAConfig.getAfterSha(rootKey, worKey);
}
@Bean(name = "jasyptStringEncryptor")
public StringEncryptor setConfig() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(buildKey());//自定义加密密钥,
//config.setPassword("abcd1234");//自定义加密密钥,
config.setAlgorithm(ALGRI);//设置加密算法
config.setKeyObtentionIterations(ITERATIONS);//设置应用于获取加密密钥的哈希迭代次数
config.setPoolSize("1");//设置要创建的加密程序池的大小
config.setProviderName("SunJCE");//设置要请求加密算法的安全提供程序的名称
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");//设置 Sal 发生器
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");//设置 IV 发生器
config.setStringOutputType("base64");//设置字符串输出的编码形式
encryptor.setConfig(config);
return encryptor;
}
}
```
注意这里Bean的名字name是需要显式指定的(默认的名字是jasyptStringEncryptor),如果像这里一样用的自定义名字,则还需要在Spring Boot的application.properties配置文件中来指定bean的名字
#### 3. 不想用自定义的生产密钥,可以在运行时导入(供下次参考选择)
直接将加密密钥从配置文件中拿掉,取而代之的有三种方式:
方式一:直接作为程序启动时的命令行参数来带入

方式二:直接作为程序启动时的应用环境变量来带入

方式三:甚至可以作为系统环境变量的方式来带入
比方说,我们提前设置好系统环境变量JASYPT_ENCRYPTOR_PASSWORD = CodeSheep,则直接在Spring Boot的项目配置文件中做如下配置即可:

### 2.7 遇到问题 (缺少操作的发生的问题)
```text
Description:
Failed to bind properties under 'spring.datasource.password' to java.lang.String:
Reason: either 'jasypt.encryptor.password', one of ['jasypt.encryptor.private-key-string', 'jasypt.encryptor.private-key-location'] for asymmetric encryption, or one of ['jasypt.encryptor.gcm-secret-key-string', 'jasypt.encryptor.gcm-secret-key-location', 'jasypt.encryptor.gcm-secret-key-password'] for AES/GCM encryption must be provided for Password-based or Asymmetric encryption
Action:
Update your application's configuration
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'studentService';
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'studentServiceImpl': Injection of resource dependencies failed;
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentMapper' defined in file [E:\IdeaPlace\backDemo\Profile_Encryption\target\classes\com\example\profile_encryption\mappers\StudentMapper.class]:
Unsatisfied dependency expressed through bean property 'sqlSessionFactory';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Unsatisfied dependency expressed through method 'sqlSessionFactory' parameter 0;
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Unsatisfied dependency expressed through method 'dataSource' parameter 0;
nested exception is org.springframework.boot.context.properties.ConfigurationPropertiesBindException:
Error creating bean with name 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties':
Could not bind properties to 'DataSourceProperties' : prefix=spring.datasource, ignoreInvalidFields=false, ignoreUnknownFields=true;
nested exception is org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'spring.datasource.password' to java.lang.String
```
解决方法:原来是缺少部分操作
1) 在启动类加入注解:@EnableEncryptableProperties
```java
@EnableEncryptableProperties
@SpringBootApplication(scanBasePackages = "com.example.profile_encryption")
@MapperScan({"com.example.profile_encryption.**.*Mapper",
"com.example.profile_encryption.mappers"})
public class ProfileEncryptionApplication{
//....
}
```
2) 配置类EncryptorCfg的setConfig方法注解@Bean需要设置name的值
```java
@Component
public class EncryptorCfg {
//...
@Bean(name = "jasyptStringEncryptor")
public StringEncryptor setConfig() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(buildKey());//自定义加密密钥, 密文加盐
config.setAlgorithm(ALGRI);//设置加密算法
config.setKeyObtentionIterations(ITERATIONS);//设置应用于获取加密密钥的哈希迭代次数
config.setPoolSize("1");//设置要创建的加密程序池的大小
config.setProviderName("SunJCE");//设置要请求加密算法的安全提供程序的名称
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");//设置 Sal 发生器
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");//设置 IV 发生器
config.setStringOutputType("base64");//设置字符串输出的编码形式
encryptor.setConfig(config);
return encryptor;
}
}
```