Jasypt加密SpringBoot配置文件和自动加密数据库敏感信息
Jasypt
是开源的加密和解密的组件。和Spring提供了很好的集成。
一、加密SpringBoot配置文件
新建SpringBoot项目,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
增加Jasypt配置类JasyptConfig
:
@Configuration
public class JasyptConfig {
/**
* 配置StandardPBEStringEncryptor加解密器的配置
* @return
*/
@Bean
public EnvironmentStringPBEConfig environmentStringPBEConfig() {
EnvironmentStringPBEConfig environmentStringPBEConfig = new EnvironmentStringPBEConfig();
/**
* 设置算法
*/
environmentStringPBEConfig.setAlgorithm("PBEWithMD5AndDES");
/**
* 从JVM环境变量中读取APP_ENCRYPTION_PASSWORD的值作为密码
*/
environmentStringPBEConfig.setPasswordSysPropertyName("APP_ENCRYPTION_PASSWORD");
return environmentStringPBEConfig;
}
/**
* 配置StandardPBEStringEncryptor加解密器
* @return
*/
@Bean("jasyptStringEncryptor")
public StandardPBEStringEncryptor standardPBEStringEncryptor() {
StandardPBEStringEncryptor standardPBEStringEncryptor = new StandardPBEStringEncryptor();
standardPBEStringEncryptor.setConfig(environmentStringPBEConfig());
return standardPBEStringEncryptor;
}
}
注意:jasyptStringEncryptor
bean名字不能修改。StandardPBEStringEncryptor的密码通过配置EnvironmentStringPBEConfig从JVM环境变量APP_ENCRYPTION_PASSWORD
获取。
在application.properties
配置端口:
server.port=8500
在Controller中增加:
@Autowired
private StringEncryptor jasyptStringEncryptor;
@RequestMapping("/encrypt")
public String encrypt(String str) {
if (str != null) {
return jasyptStringEncryptor.encrypt(str);
} else {
return null;
}
}
用于返回加密字符串。str是明文。在启动类的JVM参数中添加-DAPP_ENCRYPTION_PASSWORD=dffg438765
,等号后面的值自己修改。启动后访问http://localhost:8500/encrypt?str=123
,复制得到的结果。
在application.properties
配置:
my.value=ENC(aBGbIXKfNcOPcdB1FQbNVg==)
括号里面的值是上面的加密字符串。在Controller中增加:
@Value("${my.value}")
private String myValue;
@RequestMapping("/myValue")
public String myValue() {
return myValue;
}
重启后访问http://localhost:8500/myValue
,看到123。
application.properties
中的ENC
是否可以修改呢?Jasypt配置由JasyptEncryptorConfigurationProperties
配置,看下JasyptEncryptorConfigurationProperties类:
private String bean = "jasyptStringEncryptor";
@NestedConfigurationProperty
private PropertyConfigurationProperties property;
这里只展示两个字段。jasyptStringEncryptor
是加密和解密配置文件中的加密数据使用的Bean名。再看PropertyConfigurationProperties类:
private String detectorBean = "encryptablePropertyDetector";
private String resolverBean = "encryptablePropertyResolver";
private String filterBean = "encryptablePropertyFilter";
private String prefix = "ENC(";
private String suffix = ")";
可以看到前缀是ENC(
,后缀是)
。所以要修改ENC前缀可在application.properties
增加配置:
my.value=my_enc(aBGbIXKfNcOPcdB1FQbNVg==)
jasypt.encryptor".property.prefix=my_enc(
重启后查看效果。
二、自动加密数据库敏感字段
数据库表中保存的敏感字段,比如姓名,证件号码,手机号等。如果明文展示或泄露就会导致数据泄露造成麻烦。可以在保存时,查询时加密和解密字段。这里用Postgresql
数据库做演示。用MybatisPlus
操作数据库,也可以用Mybatis
。
添加依赖:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
在application.properties
增加数据库配置和mybatisPlus的mapper的sql文件位置:
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=my_enc(8uct/atZocNDwefc0VoS+DJO7Xt7a4ss)
mybatis-plus.mapper-locations=classpath*:mapper/*.xml
在启动类添加:
@MapperScan("com.example.mytest.mapper")
mybatisPlus扫描Mapper接口包路径。
添加实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("t_user")
public class UserEntity {
/**
* 主键
*/
@TableId(type=IdType.AUTO)
private Long idKey;
/**
* 姓名
*/
private String name;
/**
* 手机号
*/
private String phone;
}
表名是t_user
,主键是idKey,自动生成。同时使用了lombok注解。
增加Mapper接口:
public interface UserMapper extends BaseMapper<UserEntity> {
}
在数据库新增表:
create table t_user (
id_key bigserial primary key,
name varchar(50),
phone varchar(100)
);
bigserial相当于Oracle的序列,可以自动生成。
加密和解密数据库敏感字段是使用Mybatis
的plugins
。MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
Executor是执行sql时,ParameterHandler是处理参数,ResultSetHandler是映射结果,StatementHandler是负责处理MyBatis与JDBC之间Statement的交互。通过 MyBatis
提供的强大机制,使用插件
是非常简单的,只需实现 Interceptor
接口,并指定想要拦截
的方法签名
即可。要自动加解密数据敏感字段,只需要拦截ParameterHandler
和ResultSetHandler
即可。
新增注解EnCryptData
:
/**
* 要加密和解密的类加这个注解
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnCryptData {
}
要自动加密和解密的类上加这个注解。
新增注解EnCryptField
:
/**
* 要加密和解密的字段加这个注解
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnCryptField {
}
在自动加密和解密的字段加这个注解。
保存或更新时自动加密数据,要拦截ParameterHandler,增加自动加密拦截器EnCrpytPlugin
:
@Intercepts({@Signature(
type= ParameterHandler.class,
method = "setParameters",
args = PreparedStatement.class)})
@Component
@Slf4j
public class EnCrpytPlugin implements Interceptor {
@Autowired
private StringEncryptor jasyptStringEncryptor;
public Object intercept(Invocation invocation) throws Throwable {
ParameterHandler parameterHandler = (ParameterHandler)invocation.getTarget();
Object parameterObject = parameterHandler.getParameterObject();
if (parameterObject != null) {
Class<?> aClass = parameterObject.getClass();
log.info("parameterHandler:{}",aClass);
EnCryptData enCryptData = aClass.getAnnotation(EnCryptData.class);
log.info("enCryptData:{}",enCryptData);
if (enCryptData != null) {
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
EnCryptField enCryptField = field.getAnnotation(EnCryptField.class);
if (enCryptField != null) {
field.setAccessible(true);
Object o = field.get(parameterObject);
log.info("field:{},o:{}",field.getName(), o);
/**
* 暂时只支持String类型加密
*/
field.set(parameterObject, o != null ? jasyptStringEncryptor.encrypt(o.toString()) : null);
}
}
}
}
return invocation.proceed();
}
}
思路就是设置参数
时将参数类
上有@EnCryptData
同时字段有@EnCryptField
将加密后的值替换原来的值。将EnCrpytPlugin加入Spring管理即可。
修改UserEntity:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("t_user")
@EnCryptData
public class UserEntity {
/**
* 主键
*/
@TableId(type=IdType.AUTO)
private Long idKey;
/**
* 姓名
*/
private String name;
/**
* 手机号
*/
@EnCryptField
private String phone;
}
修改Controller:
@Autowired
private UserMapper userMapper;
@RequestMapping("/addUser")
public String addUser() {
UserEntity user = UserEntity.builder()
.name("张三")
.phone("13100000000")
.build();
userMapper.insert(user);
return "success";
}
@RequestMapping("/listUser")
public List<UserEntity> listUser() {
return userMapper.selectList(Wrappers.lambdaQuery(UserEntity.class));
}
addUser用于新增用户,listUser用于查询所有用户。在浏览器先执行http://localhost:8500/addUser
,在执行http://localhost:8500/listUser
,看到:
[{"idKey":1,"name":"张三","phone":"xN8bEY4pndmLd2nV5mZYf5RI9IbdqKYC"}]
可知phone已经自动加密。
现在增加自动解密插件:
@Intercepts({@Signature(
type= ResultSetHandler.class,
method = "handleResultSets",
args = Statement.class)})
@Component
@Slf4j
public class DeCrpytPlugin implements Interceptor {
@Autowired
private StringEncryptor jasyptStringEncryptor;
public Object intercept(Invocation invocation) throws Throwable {
Object resultObject = invocation.proceed();
if (resultObject == null) {
return null;
}
log.info("返回对象类型:{}", resultObject.getClass());
if (resultObject instanceof List) {
List list = (List) resultObject;
if (list.isEmpty()) {
return list;
}
/**
* 获取List元素Class
*/
Class<?> aClass = list.get(0).getClass();
EnCryptData enCryptData = aClass.getAnnotation(EnCryptData.class);
if (enCryptData != null) {
Field[] declaredFields = aClass.getDeclaredFields();
for (Object o : list) {
for (Field field : declaredFields) {
EnCryptField enCryptField = field.getAnnotation(EnCryptField.class);
if (enCryptField != null) {
field.setAccessible(true);
Object o1 = field.get(o);
log.info("field:{},o1:{}",field.getName(), o1);
field.set(o, o1 != null ? jasyptStringEncryptor.decrypt(o1.toString()) : null);
}
}
}
}
}
return resultObject;
}
}
思路和EnCrpytPlugin。区别是在映射结果
时将解密数据替换原来的值。访问http://localhost:8500/listUser
,看到:
[{"idKey":2,"name":"张三","phone":"13100000000"}]
数据已经自动解密。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署