MyBatis学习笔记-数据加密解密

如果需要对一些特殊数据进行加密、解密处理,查阅部分资料文档,一般会避开在复杂的业务层处理,大部分会在数据访问层进行数据的加密、解密处理。目前比较普遍的两种方式如下:
一、基于自定义类型转换器
主要是使用mybatis框架提供的TypeHandler来实现在持久层处理数据。
  • 实现方式
    • 持久化实体对象类中对应字段@TableField注解上新增typeHandler属性配置即可。
    • Mapper映射文件resultMap标签下对应字段result标签新增typeHandler属性配置即可。
  • 注意事项
    • 目前验证支持mybatis框架自带的语句可以生效,部分情况也失效,如查询条件包含需要加密解密的字段。
    • 不支持自定义语句。
@Slf4j
@MappedTypes({String.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class AesEncryptDecryptTypeHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, AesUtils.encryptToHex(parameter, Constants.AES_SECRET));
    }

    @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));
    }

    private String decrypt(String value) {
        if (null == value || value.length() == 0) {
            return null ;
        }
        try {
            value = AesUtils.decryptFromHex(value, Constants.AES_SECRET);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return value;
    }

}
二、基于拦截器
主要是使用mybatis框架提供的Interceptor来实现在相关操作过程的数据处理。
  • 实现方式
    • 扩展Interceptor功能,拦截相关操作进行筛选判断实现加密解密。
  • 注意事项
    • 存在多次判断、反射等操作可能存在效率问题。
    • 多种情况还需要单独处理,如查询加密解密字段等。
@Slf4j
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)})
public class ParameterEncryptInterceptor extends ParameterInterceptor implements Interceptor, InitializingBean {

    private EncryptDecryptProperties encryptDecryptProperties;

    private RsaSolver rsaSolver;

    public ParameterEncryptInterceptor(EncryptDecryptProperties encryptDecryptProperties) {
        this.encryptDecryptProperties = encryptDecryptProperties;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        EncryptDecryptProperties.AlgorithmConfig algorithmConfig = encryptDecryptProperties.getAlgorithmConfig();
        if (Constants.ALGORITHM_RSA.equalsIgnoreCase(algorithmConfig.getName())) {
            String rsaBeanName = algorithmConfig.getRsaBeanName();
            if (CharSequenceUtil.isNotBlank(rsaBeanName) && applicationContext.containsBean(rsaBeanName)) {
                rsaSolver = applicationContext.getBean(rsaBeanName, RsaSolver.class);
            }
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return handleInvocation(invocation);
    }

    @Override
    public Map<String, List<String>> getObjectFieldsMap() {
        return encryptDecryptProperties.getObjectFieldsMap();
    }

    @Override
    public Object handleObjectField(Object objectFieldValue) {
        // 暂时只处理字符串类型
        String objectFieldStringValue = String.valueOf(objectFieldValue);
        EncryptDecryptProperties.AlgorithmConfig algorithmConfig = encryptDecryptProperties.getAlgorithmConfig();
        String algorithmName = algorithmConfig.getName().toUpperCase();
        String algorithmSecret = algorithmConfig.getSecret();
        switch (algorithmName) {
            case Constants.ALGORITHM_DES:
                objectFieldStringValue = DesUtils.encryptBy3DesToHex(objectFieldStringValue, algorithmSecret);
                break;
            case Constants.ALGORITHM_AES:
                objectFieldStringValue = AesUtils.encryptToHex(objectFieldStringValue, algorithmSecret);
                break;
            case Constants.ALGORITHM_RSA:
                objectFieldStringValue = rsaSolver.encrypt(objectFieldStringValue.getBytes(StandardCharsets.UTF_8));
                break;
            default:
                break;
        }
        return objectFieldStringValue;
    }

}
@Slf4j
public abstract class ParameterInterceptor extends AbstractInterceptor {

    /**
     * handle invocation
     * @param invocation
     * @return
     * @throws Throwable
     */
    protected Object handleInvocation(Invocation invocation) throws Throwable {
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(parameterHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
            SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        Object parameterObject = parameterHandler.getParameterObject();
        if (null == parameterObject) {
            return invocation.proceed();
        }
        Class<Object> parameterizedType = getParameterizedType((MappedStatement) metaObject.getValue("mappedStatement"));
        List<String> fields = getObjectFieldsMap().get(parameterizedType.getName());
        if (null == fields || fields.isEmpty()) {
            return invocation.proceed();
        }
        if (parameterObject instanceof Map) {
            Map<String, Object> parameterMapObject = (Map<String, Object>) parameterObject;
            if (parameterMapObject.containsKey(com.baomidou.mybatisplus.core.toolkit.Constants.ENTITY)) {
                // handle entity object
                Object etObject = parameterMapObject.get(com.baomidou.mybatisplus.core.toolkit.Constants.ENTITY);
                if (null != etObject) {
                    handleObjectFields(etObject);
                }
            } if (parameterMapObject.containsKey(com.baomidou.mybatisplus.core.toolkit.Constants.WRAPPER)) {
                // handle wrapper object
                handleObjectFields(metaObject, fields);
            } else {
                // handle map object
                handleObjectFields(parameterizedType, parameterMapObject);
            }
        } else {
            handleObjectFields(parameterObject);
        }
        return invocation.proceed();
    }

}
对于数据加密,Spring Boot和MyBatis可以结合使用。以下是一种常见的数据加密方案: 1. 在Spring Boot项目中,可以使用Java加密库(如Jasypt、Bouncy Castle等)来进行数据加密。这些库提供了各种加密算法和工具类,可以轻松地实现数据的加密解密操作。 2. 在MyBatis中,可以通过自定义类型处理器(TypeHandler)来实现对加密数据的处理。自定义类型处理器可以将数据库中的加密数据解密为明文,并在向数据库插入数据时将明文加密为密文。 下面是一个示例代码,演示了如何在Spring Boot和MyBatis中实现数据加密功能: 1. 创建一个加密工具类,例如 EncryptUtils,用于封装加密解密操作的方法。这个类可以使用任何合适的加密库来实现具体的加密算法。 ```java public class EncryptUtils { // 使用加密库进行数据加密 public static String encrypt(String data) { // 实现加密算法 // ... return encryptedData; } // 使用加密库进行数据解密 public static String decrypt(String encryptedData) { // 实现解密算法 // ... return decryptedData; } } ``` 2. 创建一个自定义类型处理器,在MyBatis中使用这个处理器来处理加密数据的读写操作。 ```java @MappedTypes(String.class) public class EncryptedStringTypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { // 将明文数据加密后存入数据库 ps.setString(i, EncryptUtils.encrypt(parameter)); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { // 从数据库读取加密数据并解密 return EncryptUtils.decrypt(rs.getString(columnName)); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { // 从数据库读取加密数据并解密 return EncryptUtils.decrypt(rs.getString(columnIndex)); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { // 从数据库读取加密数据并解密 return EncryptUtils.decrypt(cs.getString(columnIndex)); } } ``` 3. 在MyBatis的配置文件中注册自定义类型处理器。 ```xml <typeHandlers> <typeHandler handler="com.example.EncryptedStringTypeHandler"/> </typeHandlers> ``` 4. 在Spring Boot中配置MyBatis,使其加载自定义类型处理器。 ```java @Configuration @MapperScan("com.example.mapper") public class MyBatisConfig { @Bean public ConfigurationCustomizer mybatisConfigurationCustomizer() { return configuration -> configuration.getTypeHandlerRegistry().register(String.class, EncryptedStringTypeHandler.class); } } ``` 通过以上步骤,您就可以在Spring Boot项目中使用MyBatis进行数据库操作,并对需要加密的数据进行加密解密处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值