baomidou / mybatis-plus

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

QueryWrapper查询时,自动调用了TypeHandler对参数进行了加密 #6317

Closed okayangel closed 3 months ago

okayangel commented 3 months ago

当前使用版本 3.5.1 当前环境信息 Java8 + Mysql5.7

描述bug现象 通过自定义TypeHandler实现数据库字段加解密,加密密钥通过spring注入后,使用QueryWrapper添加的查询条件,传入的值全部自动进行了加密,没有设置typehandler的列也加密了。

在没有通过Spring注入的时候,将密钥写死,不会出现这个问题

提供问题复现步骤

public class CryptoUtils {
    private String secret;

    public CryptoUtils(String secret) {
        this.secret = secret;
    }

    public String encryptBase64(String value) {
        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, secret.getBytes());
        return aes.encryptBase64(value);
    }

    public String decryptStr(String columnValue) {
        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, secret.getBytes());
        return aes.decryptStr(columnValue);
    }
}
public class EncryptDecryptTypeHandler extends BaseTypeHandler<String> {

    private final CryptoUtils cryptoUtils;

    public EncryptDecryptTypeHandler(CryptoUtils cryptoUtils) {
        this.cryptoUtils = cryptoUtils;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String value, JdbcType jdbcType) throws SQLException {
        ps.setString(i, cryptoUtils.encryptBase64(value));
    }
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String columnValue = rs.getString(columnName);
        if (StringUtil.isNotEmpty(columnValue)) {
            return cryptoUtils.decryptStr(columnValue);
        } else {
            return "";
        }
    }
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String columnValue = rs.getString(columnIndex);
        if (StringUtil.isNotEmpty(columnValue)) {
            return cryptoUtils.decryptStr(columnValue);
        } else {
            return "";
        }
    }
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String columnValue = cs.getString(columnIndex);
        if (StringUtil.isNotEmpty(columnValue)) {
            return cryptoUtils.decryptStr(columnValue);
        } else {
            return "";
        }
    }
}
@Configuration
public class MybatisPlusConfig {
    @Value("${db.secret}")
    private String secret;

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public EncryptDecryptTypeHandler encryptDecryptTypeHandler(CryptoUtils cryptoUtils) {
        EncryptDecryptTypeHandler handler = new EncryptDecryptTypeHandler(cryptoUtils);
        return handler;
    }

    @Bean
    public CryptoUtils cryptoUtils() {
        return new CryptoUtils(secret);
    }

}
@Data
@TableName(value = "t_clinician", autoResultMap = true)
public class GdiClinicianEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 医师ID
     */
    @TableId(value = "Clinician_ID", type = IdType.INPUT)
    private Long clinicianId;
    /**
     * 医师编号
     */
    private String clinicianNo;
    /**
     * 医师姓名
     */
    private String clinicianName;
    /**
     * 创建时间
     */
    private Date createdTime;
    /**
     * 证件号码
     */
    @TableField(typeHandler = EncryptDecryptTypeHandler.class)
    private String idcertNo;
    /**
     * 手机号码
     */
    @TableField(typeHandler = EncryptDecryptTypeHandler.class)
    private String mobileNo;
}
QueryWrapper<GdiClinicianDto> wrapper = new GQueryWrapper<GdiClinicianDto>().getWrapper(params);
        String keyword = Convert.toStr(params.get("keyword"));
        if (StrUtil.isNotBlank(keyword)) {
            wrapper.like("t_clinician.Clinician_No", keyword);
        }
IPage<GdiClinicianDto> page = this.baseMapper.queryDtoPage(new Query<GdiClinicianDto>().getPage(params), wrapper);

调用queryDtoPage查询时,传入的keyword是未加密的值,执行的语句在日志中显示已经加传入的值进行了加密。

提供完整堆栈日志(可选) 2024-07-08 16:03:31.598 [TID: N/A] [XNIO-1 task-1] DEBUG c.f.m.p.g.d.G.queryDtoPage_mpCount -==> Preparing: SELECT COUNT(*) AS total FROM t_clinician WHERE (t_clinician.Clinician_No LIKE ?) 1371951 2024-07-08 16:03:32.339 [XNIO-1 task-1] DEBUG cn.med.dao.ClinicianDao.queryDtoPage_mpCount [137] - ==> Parameters: ZoGjZjFZ3Pv+XuaEj7giow==(String) 2024-07-08 16:03:32.339 [TID: N/A] [XNIO-1 task-1] DEBUG c.f.m.p.g.d.G.queryDtoPage_mpCount -==> Parameters: ZoGjZjFZ3Pv+XuaEj7giow==(String)

提供问题复现工程(可选) 请尽量提供复现工程,减少大家排错的时间.

miemieYaho commented 3 months ago

你的typehandler别加入spring,你把mybatis的默认string的typehandler顶掉了

okayangel commented 3 months ago

你的typehandler别加入spring,你把mybatis的默认string的typehandler顶掉了

就是想实现密钥通过配置文件设置,如果写死在代码中是没有问题

felix9ia commented 5 days ago

@okayangel 看起来你的 CryptoUtils 配置项入侵的太深了,在 Repo 层注入后搞不就行了?如果多个地方要用,就抽象个 BaseRepo 不就行了...