扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
加密:将明文信息改变为难以读取的密文内容。
解密:将密文内容转化为原来数据。创新互联建站主营肇州网站建设的网络公司,主营网站建设方案,重庆App定制开发,肇州h5成都微信小程序搭建,肇州网站营销推广欢迎肇州等地区企业咨询
分类
相关阅读:
说明
- 数据库存储密文字段,内存可见为明文信息
- 可设定字段保存值支持 整体加密(仅可全部匹配查询)、模糊加密(支持模糊查询)
- 相关做法: 数据库隐私字段加密以及加密后的数据如何进行模糊查询? - 业余草
实现做法:常规二,对明文数据进行分词组合,将分词组合的结果集分别进行加密,然后存储到扩展列,查询时通过 column LIKE '%partial%'
分组规则:按固定长度,根据4位英文字符(半角),2个中文字符(全角)为一个检索条件
分组示例:ningyu1 使用4个字符为一组的加密方式 -> ning + ingy + ngyu + gyu1
整体加密数据库字段长度与保存值长度对应(需要额外存储一个标识符)
数据库字段长度 | 字符长度 |
---|---|
varchar(49) | 12个中文字符、36个ascii字符 |
varchar(97) | 24个中文字符、72个ascii字符、3个ascii字符+23个中文字符、68个ascii字符+1个中文字符 |
varchar(197) | 49个中文字符、145个ascii字符、3个ascii字符+48个中文字符 、 142个ascii字符+1个中文字符 |
varchar(253) | 63个中文字符、189个ascii字符 、3个ascii字符+62个中文字符 、 186个ascii字符+1个中文字符 |
模糊加密数据库字段长度与保存值长度对应(4个字节加密一次大致对应8个字符+1个标识符),根据4位英文字符(半角),2个中文字符(全角)为一个检索条件
数据库字段长度 | 字符长度 |
---|---|
varchar(99) | 12个中文字符、14个ascii字符 |
varchar(198) | 23个中文字符、25个ascii字符 |
varchar(252) | 29个中文字符、31个ascii字符 |
INSERT VALUE、UPDATE ENTITY、SELECT RESULT
只对数据库和程序之间的数据转换,查询条件不会调用。
整体加密类型处理器
使用示例:
1. MyBatis-Plus 注解(自动生产 ResultMap ,存在场景不生效)
@TableField(typeHandler = OverallCryptoTypeHandler.class)
2. 自定义 ResultMap 配置
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
/*
对非null参数值进行加密,需要通过实体类处理方可,支持 INSERT/UPDATE ENTITY
当前处理 INSERT ENTITY,UPDATE ENTITY 会先通过拦截器处理
因为拦截器修改元数据将导致实体类属性值产生变更,所以实体类还是由 TypeHandler 来进行处理
*/
ps.setString(i, CryptoDataUtil.overallEncrypt(parameter));
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 对可为null的结果进行解密
return CryptoDataUtil.decrypt(rs.getString(columnName));
}
模糊加密类型处理器
注意:根据4位英文字符(半角),2个中文字符(全角)为一个检索条件,如果字段值较少查询可能存在问题
使用示例:
1. MyBatis-Plus 注解(自动生产 ResultMap ,存在场景不生效)
@TableField(typeHandler = FuzzyCryptoTypeHandler.class)
2. 自定义 ResultMap 配置
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
/*
对非null参数值进行加密,需要通过实体类处理方可,支持 INSERT/UPDATE ENTITY
当前处理 INSERT ENTITY,UPDATE ENTITY 会先通过拦截器处理
因为拦截器修改元数据将导致实体类属性值产生变更,所以实体类还是由 TypeHandler 来进行处理
*/
ps.setString(i, CryptoDataUtil.fuzzyEncrypt(parameter));
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 对可为null的结果进行解密
return CryptoDataUtil.decrypt(rs.getString(columnName));
}
注:目前仅支持简单查询处理,复杂查询可能存在问题。
自定义字段注解 CryptoCond.java
replacedColumn()
替换 SQL 查询条件中的字段名encryption()
对 SQL 中条件值、参数值进行加密,支持两种方式(整体匹配、模糊匹配)User.java
自定义 MyBatis 拦截器
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement =
(MappedStatement) metaObject.getValue("delegate.mappedStatement");
// 支持处理 SELECT、UPDATE、DELETE
boolean canHandler = Stream.of(SqlCommandType.SELECT, SqlCommandType.UPDATE,
SqlCommandType.DELETE)
.anyMatch(item -> item.equals(mappedStatement.getSqlCommandType()));
if (canHandler && !getIntercept()) {
clearIntercept();
return invocation.proceed();
}
clearIntercept();
// 判断是否有参数需要处理
BoundSql boundSql = statementHandler.getBoundSql();
if (Objects.isNull(boundSql.getParameterObject())) {
return invocation.proceed();
}
// 获取自定义注解,通过 MapperID 获取到 Mapper 对应的实体类,获取实体类所有注解字段与注解对应 Map
Map condMap = mapEntityFieldCond(mappedStatement.getId());
if (CollectionUtil.isNotEmpty(condMap)) {
replaceHandle(mappedStatement.getConfiguration(), condMap, boundSql);
}
return invocation.proceed();
}
// 替换数据处理
private void replaceHandle(Configuration configuration, Map condMap,
BoundSql boundSql) {
String sql = boundSql.getSql();
List operationList = SqlUtil.listSqlCondOperation(sql);
if (CollectionUtil.isEmpty(operationList)) {
return;
}
MetaObject paramMetaObject = configuration.newMetaObject(boundSql.getParameterObject());
List mappings = boundSql.getParameterMappings();
int condParamStart = SqlUtil.getSqlCondParamStartIdx(sql);
int mappingStartIdx = 0;
for (SqlCondOperation operation : operationList) {
String columnName = operation.getColumnName();
String condStr = operation.getOriginCond();
int condNum = SqlUtil.countPreparePlaceholder(condStr);
CryptoCond ann = condMap.get(operation.getColumnName());
if (Objects.nonNull(ann)) {
// 替换查询条件参数中的列名
if (StrUtil.isNotBlank(ann.replacedColumn())
&& condParamStart < operation.getOriginCondStartIdx()) {
sql = sql.replace(condStr,
condStr.replace(columnName, ann.replacedColumn()));
}
// 替换属性值为加密值
if (condNum == 0) {
// 存在非预编译语句条件,直接替换 SQL 条件值
String propVal = String.valueOf(paramMetaObject.getValue(columnName));
String useVal = getCryptoUseVal(ann, propVal);
sql = sql.replace(condStr, condStr.replace(propVal, useVal));
} else {
// 预编译语句条件通过替换条件值处理
for (int i = 0; i < condNum; i++) {
String propName = mappings.get(mappingStartIdx + i).getProperty();
if (!propName.startsWith("et.")) {
// 非实体类属性进行值替换,实体类属性通过 TypeHandler 处理
String propVal = String.valueOf(paramMetaObject.getValue(propName));
paramMetaObject.setValue(propName, getCryptoUseVal(ann, propVal));
}
}
}
}
mappingStartIdx += condNum;
}
ReflectUtil.setFieldValue(boundSql, "sql", sql);
}
测试
IUserService.java
UserServiceTest.java
使用问题
TypeHandler 不起效
UserMapper.xml
@TableName(autoResultMap = true)
,自动构建 ResultMap加密后解密数据乱码
CryptoCondInterceptor 不起效
@CryptoCond
,示例:User.java
@Data
@TableName(value = "crypto_user", autoResultMap = true)
public class User {
/**
* 用户表主键ID
*/
private Long id;
/**
* 用户名
*/
private String name;
/**
* 加密后的密码,MD5加盐
*/
private String password;
/**
* 加密密码使用的盐
*/
private String salt;
/**
* 手机号码,整体加密
*/
@TableField(typeHandler = OverallCryptoTypeHandler.class)
@CryptoCond(encryption = CryptoCond.EncryptionEnum.DEFAULT_OVERALL)
private String phone;
/**
* 邮箱,模糊加密
*/
@TableField(typeHandler = FuzzyCryptoTypeHandler.class)
@CryptoCond(encryption = CryptoCond.EncryptionEnum.DEFAULT_FUZZY)
private String email;
/**
* 创建时间
*/
@TableField(fill = INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = INSERT_UPDATE)
private LocalDateTime updateTime;
}
public interface UserMapper extends BaseMapper
MySQL 异常
参考
demo 地址:https://github.com/EastX/java-practice-demos/tree/main/demo-crypto
推荐阅读:
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流