本文记录Springboot + Mybatis 架构下实现 Mysql 保存数据自动加密与查询和自动解密
本文代码均为示例代码
本文代码均为示例代码
方案1:基于注解
♾️ java 代码:基本思路
- 定义策略,基于Mybatis的类BaseTypeHandler
- 定义字段注解,配置策略
- 在需要加解密的实体字段上添加注解即可实现
说明:简单易用,但使用了反射获取注解可能对性能有一定影响
注意:此方案不会被加密的参数覆盖原始值,所以加密后访问对象依旧是原始值(数据库正常加密)
// 以下为策略类核心代码
public class SqlAutoHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
// 此处实现字段加密
ps.setString(i, AesUtil.encrypt(parameter));
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 以下方法用于解密(响应字段可能存在多加密内容拼接,可以根据业务场景兼容)
return decrypt(rs.getString(columnName));
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return decrypt(rs.getString(columnIndex));
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return decrypt(cs.getString(columnIndex));
}
}
♾️ java 代码:// 配置字段注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@TableField(typeHandler = SqlAutoHandler.class)
public @interface IndexLucene {
}
♾️ java 代码:// 使用示例
@Data
public class User {
private Long id;
@IndexLucene
private Long phone;
}
// 实体类添加注解后调用xml查询即可实现自动加解密
方案2:基于策略
♾️ java 代码:基本思路
- 不使用注解,手动配置实体类加解密属性字段
- 借助Mybatis数据持久化前置处理器实现加密MetaObjectHandler
- 使用拦截器和@Intercepts注解实现查询解密
说明:新增对象时需要对应增加实体类加密字段类
注意:此方案加密会对原始值覆盖后保存或修改,后续调用加密字段值为加密后的值
@Component
public class BeforeMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 对指定字段进行加密,从请求参数读取操作实体类,再从配置中检查对应实体类需要操作的字段名进行加密处理
}
@Override
public void updateFill(MetaObject metaObject) {
// 同上
}
}
♾️ java 代码:@Component
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class SqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 此处同时也可实现SQL语句替换,例如自定义函数转换
// SQL执行获取查询结果
Object object = invocation.proceed();
if (object instanceof Collection) {
Collection<?> collection = (Collection)object;
Iterator<?> iterator = collection.iterator();
// 通过响应实体类型获取对应实体类需要解密的字段名,此处有多种方式实现,此处忽略
while(iterator.hasNext()) {
Object item = iterator.next();
decrypt(item);
}
} else if (object instanceof Serializable) {
// 同上需要读取策略
decrypt(object);
}
return object;
}
}