baomidou / mybatis-plus

An powerful enhanced toolkit of MyBatis for simplify development
https://baomidou.com
Apache License 2.0
16.42k stars 4.31k forks source link

AESEncryptHandler继承BaseTypeHandler实现部分字段加密解密,单条数据查询时不会进入AESEncryptHandler #6107

Closed Astrsea closed 6 months ago

Astrsea commented 6 months ago

当前使用版本(必填,否则不予处理)

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.5</version>
</dependency>

该问题是如何引起的?(确定最新版也有问题再提!!!)

用原始数据查询数据时,无法自动将查询条件中的数据进行加密处理 ,但是列表查询时能自动解密插入时能自动加密

原因:
LambdaQueryWrapper中的eq方法无法对加@TableField(value = "system_key",typeHandler = AESEncryptHandler.class)进行加密处理

列表查询时:

==>  Preparing: SELECT COUNT(*) AS total FROM system_config t
==> Parameters: 
<==    Columns: total
<==        Row: 1
<==      Total: 1
==>  Preparing: SELECT t.system_config_id,t.system_key,t.system_value,t.description,t.created_by,t.updated_by,t.created_at,t.updated_at FROM system_config t LIMIT ?
==> Parameters: 10(Long)
<==    Columns: system_config_id, system_key, system_value, description, created_by, updated_by, created_at, updated_at
<==        Row: 1784376751312343041, S6EWmAkjdRveYd46NUYQ4w==, string, <<BLOB>>, 1111111111111111111, 1111111111111111111, 2024-04-28 08:19:04, 2024-04-28 08:19:04
<==      Total: 1

结果(能自动进行解密处理):

{
    "code": "000000",
    "msg": "OK",
    "explain": null,
    "data": {
        "pageSize": 10,
        "current": 1,
        "maxPage": 1,
        "total": 1,
        "list": [
            {
                "systemConfigId": "1784376751312343041",
                "systemKey": "string",
                "systemValue": "string",
                "description": "string"
            }
        ]
    }
}

插入时:

@Override
    public SystemConfig insert0(SystemConfig systemConfig) {
        SystemConfig systemConfig1 = selectOneBySystemKey(systemConfig.getSystemKey());
        if (Objects.isNull(systemConfig1)) {
            if (systemConfigMapper.insert(systemConfig) == 0) {
                throw new BusinessException(CodeEnums.S00000);
            }
            return systemConfig;
        }
        return systemConfig1;
    }

能自动进行加密处理

==>  Preparing: INSERT INTO system_config ( system_config_id, system_key, system_value, description, created_by, updated_by, created_at, updated_at ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )
==> Parameters: 1784398913226190850(String), S6EWmAkjdRveYd46NUYQ4w==(String), string(String), string(String), 1111111111111111111(String), 1111111111111111111(String), 2024-04-28T09:47:08.100658900(LocalDateTime), 2024-04-28T09:47:08.100658900(LocalDateTime)

重现步骤(如果有就写完整)

Server

@Override
    public SystemConfig selectOneBySystemKey(String systemKey) {
        LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SystemConfig::getSystemKey,systemKey);
        return systemConfigMapper.selectOne(wrapper);
    }

AESEncryptHandler 代码:

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes({String.class})
public class AESEncryptHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        EncryptProperty encryptProperty = BeanUtil.getBean(EncryptProperty.class);
        String content = StringUtils.isBlank(parameter) ? "" : parameter;
        ps.setString(i,EncryptUtil.aesEncryptData(encryptProperty.getAesKey(),content));
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        EncryptProperty encryptProperty = BeanUtil.getBean(EncryptProperty.class);
        return EncryptUtil.aesDecryptDataString(encryptProperty.getAesKey(),rs.getString(columnName));
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        EncryptProperty encryptProperty = BeanUtil.getBean(EncryptProperty.class);
        String columnValue = rs.getString(columnIndex);
        return EncryptUtil.aesEncryptData(encryptProperty.getAesKey(),columnValue);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        EncryptProperty encryptProperty = BeanUtil.getBean(EncryptProperty.class);
        String columnValue = cs.getString(columnIndex);
        return EncryptUtil.aesEncryptData(encryptProperty.getAesKey(),columnValue);
    }
}

实体类中加密属性:

@TableField(value = "system_key",typeHandler = AESEncryptHandler.class)
    private String systemKey;

Mapper.xml:

<result property="systemKey" column="system_key" jdbcType="VARCHAR" typeHandler="cloud.astrsea.preprocessing.handler.AESEncryptHandler"/>

请求参数

{
  "systemKey": "string",
}

报错信息

数据未被加密

==>  Preparing: SELECT system_config_id,system_key,system_value,description,created_by,updated_by,created_at,updated_at FROM system_config WHERE (system_key = ?)
==> Parameters: string(String)
<==      Total: 0
shan-liangguang commented 6 months ago

复现了一下最新版3.5.6也存在这个问题

shan-liangguang commented 6 months ago

问题发生在AbstractWrapper.addCondition()和AbstractWrapper.formatParam()中,本应该拼接成 system_key=#{systemKey,typeHandler=cloud.astrsea.preprocessing.handler.AESEncryptHandler},却拼接成system_key=#{ew.paramNameValuePairs.MPGENVAL1}

Astrsea commented 6 months ago

问题发生在AbstractWrapper.addCondition()和AbstractWrapper.formatParam()中,本应该拼接成 system_key=#{systemKey,typeHandler=cloud.astrsea.preprocessing.handler.AESEncryptHandler},却拼接成system_key=#{ew.paramNameValuePairs.MPGENVAL1}

那这个应该怎么处理

shan-liangguang commented 6 months ago

问题发生在AbstractWrapper.addCondition()和AbstractWrapper.formatParam()中,本应该拼接成 system_key=#{systemKey,typeHandler=cloud.astrsea.preprocessing.handler.AESEncryptHandler},却拼接成system_key=#{ew.paramNameValuePairs.MPGENVAL1}

那这个应该怎么处理

我不知道这个是特性还是bug,@nieqiurong 如果是bug的话,希望能让我提交pr

sirius19 commented 6 months ago

使用姿势不对吧。看代码注释说typeHandler要配合 TableName.autoResultMap() 一起使用

Astrsea commented 6 months ago

使用姿势不对吧。看代码注释说typeHandler要配合 TableName.autoResultMap() 一起使用

我这边在mapper里面用了,所以autoResultMap = true可以不使用

<result property="systemKey" column="system_key" jdbcType="VARCHAR" typeHandler="cloud.astrsea.preprocessing.handler.AESEncryptHandler"/>
btredback commented 6 months ago

新版本查询有说支持query自动调用handler吗?看原来issue用法如下: https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java testLambdaTypeHandler

QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.lambda() .apply("cert_no={0,typeHandler="

shan-liangguang commented 6 months ago

我已找到select自动调用handler的方法

Astrsea commented 6 months ago

@miemieYaho 那能否让LambdaQueryWrapper中自动handler,使查询值自动加密,实现加密字段的查询

miemieYaho commented 6 months ago

用wrapper.apply("cert_no={0,typeHandler="+ EncryptHandler.class.getCanonicalName()+ "}", "test1234");

limng06 commented 4 months ago

用wrapper.apply("cert_no={0,typeHandler="+ EncryptHandler.class.getCanonicalName()+ "}", "test1234");

为什么要使用这种复杂又难写的方式呢,如果能自动适配不是更好?是考虑什么原因