mybatis拦截器实现自定义注解的敏感信息加密

2022-12-07  本文已影响0人  一二追

目录

1.开发环境与工具

2.自定义注解接口:

2.1敏感类的注解接口(SensitiveData)

2.2敏感字段的注解接口(SensitiveField)

2.3注解接口的用法

3.注解注入后需要写加解密的拦截器

3.1加密拦截器(EncryptInterceptor)

3.2解密拦截器(DecryptInterceptor)

4.configuration的jar包的拦截器的注册
1.开发环境与工具

java,idea,mybatis,spring boot

2.自定义注解接口: 2.1敏感类的注解接口(SensitiveData)

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.annotation.*;

@documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SensitiveData {
}

2.2敏感字段的注解接口(SensitiveField)

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.annotation.*;

@documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SensitiveField {
}

2.3注解接口的用法
举个例子,用户表(SysUser)中的手机(phone)字段需要进行加密处理,则需要在SysUser上加@SensitiveData进行注解,另外在phone字段上加@SensitiveField进行注解。

@SensitiveData
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("sys_user")
public class SysUser extends baseEntity {

@TableId(type = IdType.ASSIGN_ID)
private Long id;


@Excel(name = "账号", width = 20)
private String account;


private String password;


@Excel(name = "姓名", width = 20)
private String name;



@SensitiveField
@Excel(name = "手机", width = 30)
private String phone;


private String salt;

}
3.注解注入后需要写加解密的拦截器
以上注解写完之后,我们需要写拦截器。让数据在后台存入数据库时进行拦截,拦截后将需要加密的字段在存入数据库之前使用SM4加密后再进行保存。同样的,在我们将数据从数据库中读出来时也需要进行拦截,拦截后使用SM4解密后拿到原值。

3.1加密拦截器(EncryptInterceptor)

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type=Executor.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class EncryptInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
        // 获取该sql语句的类型,例如update,insert
        String methodName = invocation.getMethod().getName();
        // 获取该sql语句放入的参数
        Object parameter = invocation.getArgs()[1];

        
        if (StringUtils.equalsIgnoreCase("query", methodName)) {

            
            Object result = invocation.proceed(); //执行请求方法,并将所得结果保存到result中
            if (result instanceof ArrayList) {
                ArrayList resultList = (ArrayList) result;
                for (int i = 0; i < resultList.size(); i++) {
                    SensitiveData sensitiveData = AnnotationUtils.findAnnotation(resultList.get(i).getClass(), SensitiveData.class);
                    if (Objects.nonNull(sensitiveData)) {
                        Field[] declaredFields = resultList.get(i).getClass().getDeclaredFields();


                    }
                }
            }
            return result;
        }

        // 拦截 Executor 的 update 方法 生成sql前将 tenantId 设置到实体中
        if (StringUtils.equalsIgnoreCase("update", methodName) ||
                StringUtils.equalsIgnoreCase("insert", methodName)) {

            Class type = statement.getResultMaps().get(0).getClass();
            if(parameter instanceof Map){
                parameter = ((Map) parameter).get("param1");
            }
            SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameter.getClass(), SensitiveData.class);
            if (Objects.nonNull(sensitiveData)) {

                    // 对参数内含注解的字段进行加密
                Field[] declaredFields = parameter.getClass().getDeclaredFields();
                Sm4crypto.encrypt(declaredFields, parameter);

            }
        }

        return invocation.proceed();
    }



    
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    
    @Override
    public void setProperties(Properties properties) {
    }
}

3.2解密拦截器(DecryptInterceptor)

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;

@Component
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}),
})
public class DecryptInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //取出查询的结果
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return null;
        }
        //基于selectList
        if (resultObject instanceof ArrayList) {
            ArrayList resultList = (ArrayList) resultObject;
            if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
                for (Object result : resultList) {
                    //逐一解密
                    Sm4crypto.decrypt(result);
                }
            }
            //基于selectOne
        } else {
            if (needToDecrypt(resultObject)) {
                Sm4crypto.decrypt(resultObject);
            }
        }
        return resultObject;
    }

    private boolean needToDecrypt(Object object) {
        Class objectClass = object.getClass();
        SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
        return Objects.nonNull(sensitiveData);
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

4.拦截器用到的SM4相关类 4.1 SM4加解密组件(Sm4crypto)

    /**
     * 注册加密拦截器
     */
    @Bean(value = "encryptInterceptor")
    public EncryptInterceptor encryptInterceptor(@Qualifier("sensitiveManager") SensitiveManager sensitiveManager) {
        EncryptInterceptor interceptor = new EncryptInterceptor(sensitiveManager);
        Properties properties = new Properties();
        // 可以调用properties.setProperty方法来给拦截器设置一些自定义参数
        interceptor.setProperties(properties);
        return interceptor;
    }

    /**
     * 注册解密拦截器
     */
    @Bean(value = "decryptInterceptor")
    public DecryptInterceptor decryptInterceptor(@Qualifier("sensitiveManager") SensitiveManager sensitiveManager) {
        DecryptInterceptor interceptor = new DecryptInterceptor(sensitiveManager);
        Properties properties = new Properties();
        // 可以调用properties.setProperty方法来给拦截器设置一些自定义参数
        interceptor.setProperties(properties);
        return interceptor;
    }

    @Bean(name = "filingSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("filingDataSource") DataSource masterDataSource, @Qualifier("pageInterceptor") PageInterceptor pageInterceptor,@Qualifier("encryptInterceptor") EncryptInterceptor encryptInterceptor,@Qualifier("decryptInterceptor")DecryptInterceptor decryptInterceptor)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setPlugins(pageInterceptor,encryptInterceptor,decryptInterceptor);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(FilingAutoConfiguration.MAPPER_LOCATION));

        return sessionFactory.getObject();
    }

上一篇下一篇

猜你喜欢

热点阅读