Jasypt加密SpringBoot配置文件和自动加密数据库敏感信息

Jasypt是开源的加密和解密的组件。和Spring提供了很好的集成。

一、加密SpringBoot配置文件

 
新建SpringBoot项目,添加依赖


org.springframework.boot
spring-boot-starter-web


com.github.ulisesbocchio
jasypt-spring-boot-starter
3.0.5

增加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;
}

}

注意:jasyptStringEncryptorbean名字不能修改。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

 

添加依赖:


org.postgresql
postgresql


com.baomidou
mybatis-plus-boot-starter
3.5.2


org.projectlombok
lombok
true

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 {
}

在数据库新增表:

create table t_user (
id_key bigserial primary key,
name varchar(50),
phone varchar(100)
);

bigserial相当于Oracle的序列,可以自动生成。

加密和解密数据库敏感字段是使用Mybatisplugins。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 接口,并指定想要拦截方法签名即可。要自动加解密数据敏感字段,只需要拦截ParameterHandlerResultSetHandler即可。
 

新增注解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 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"}]

数据已经自动解密。

 
 
 
 
 

参考:https://mybatis.net.cn/configuration.html#plugins

posted @ 2023-06-01 22:52  shigp1  阅读(1588)  评论(0)    收藏  举报