Use springboot + mybatis interceptor realize the ID and other production encryption and decryption of sensitive data

In actual production projects, often need information such as identity cards, phone numbers, real names and other sensitive data are encrypted database storage, but in the business code to manually encrypt and decrypt sensitive information is not very elegant, there will be even wrong encryption leakage encryption, business people need to know the actual situation of encryption rules. This article describes the use springboot + mybatis interceptors form custom annotations + detailed process sensitive data before storing the encrypted interception.

table of Contents

First, what is Mybatis Plugin

Second, the realization annotation-based encryption and decryption of sensitive information interceptor

2.1, the realization of ideas

2.2 definitions need to encrypt sensitive information declassified notes

2.3 Definitions encryption interface and its implementation class

2.4 to participate in the realization of dense interceptor

2.5 decryption defined interface and its implementation class

2.6 decryption parameters define interceptors

3, annotation requires encryption and decryption entity class field


First, what is Mybatis Plugin

In mybatis official documents , for the introduction of Mybatis plugin is this:

MyBatis mapping process allows you to have a statement execution of a point to intercept calls. By default, MyBatis allows the use of plug-ins to intercept method calls include:

    // statement execution interception

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

   // parameter acquisition, setting intercept

  • ParameterHandler (getParameterObject, setParameters)

   // returns the result to intercept

  • ResultSetHandler (handleResultSets, handleOutputParameters)

    // sql statement intercept

  • StatementHandler (prepare, parameterize, batch, update, query)

In short, that whole cycle of sql execution, we can cut to any parameter of a point to sql, sql execution result set, sql statement processing section and so on itself. Based on this characteristic, we can use the data that we need to be encrypted to be unified encryption processing section a (pagination plug-in is achieved pageHelper database paging queries).

Second, the realization annotation-based encryption and decryption of sensitive information interceptor

2.1 realization of ideas

For encryption and decryption of data, there should be two interceptors data interception

Referring to the official document, so here we should use ParameterHandler interceptor to encrypt the parameters

Use ResultSetHandler interceptors decrypt an operation reference.

Target requires encryption, decryption fields may need to be flexible to change, then we define a comment , the need for the encrypted fields annotation, the interceptors will be able to fit the data needed to perform the encryption and decryption operations.

mybatis the interceptor interface has the following methods need to achieve.

public interface Interceptor {

  //主要参数拦截方法
  Object intercept(Invocation invocation) throws Throwable;

  //mybatis插件链
  default Object plugin(Object target) {return Plugin.wrap(target, this);}

  //自定义插件配置文件方法
  default void setProperties(Properties properties) {}

}

2.2 definitions need to encrypt sensitive information declassified notes

 Defined Note sensitive information (such as the entity class POJO \ PO) annotations

/**
 * 注解敏感信息类的注解
 */
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
}

Defined Note sensitive information such sensitive fields of the annotations

/**
 * 注解敏感信息类中敏感字段的注解
 */
@Inherited
@Target({ ElementType.Field })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {
}

2.3 Definitions encryption interface and its implementation class

Custom encryption interface to facilitate future expansion of the encryption method (such as AES encryption algorithm to expand support for PBE algorithm, it can be specified only when injected)

public interface EncryptUtil {
    
    /**
     * 加密
     *
     * @param declaredFields paramsObject所声明的字段
     * @param paramsObject   mapper中paramsType的实例
     * @return T
     * @throws IllegalAccessException 字段不可访问异常
     */
     <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException;
}

 EncryptUtil AES encryption implementation class, as here AESUtil AES encryption tool from the package, can be self required partner small package not be provided herein.


@Component
public class AESEncrypt implements EncryptUtil {
    
    @Autowired
    AESUtil aesUtil;

    /**
     * 加密
     *
     * @param declaredFields paramsObject所声明的字段
     * @param paramsObject   mapper中paramsType的实例
     * @return T
     * @throws IllegalAccessException 字段不可访问异常
     */
    @Override
    public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
        for (Field field : declaredFields) {
            //取出所有被EncryptDecryptField注解的字段
            SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
            if (!Objects.isNull(sensitiveField)) {
                field.setAccessible(true);
                Object object = field.get(paramsObject);
                //暂时只实现String类型的加密
                if (object instanceof String) {
                    String value = (String) object;
                    //加密  这里我使用自定义的AES加密工具
                    field.set(paramsObject, aesUtil.encrypt(value));
                }
            }
        }
        return paramsObject;
    }
}

2.4 to participate in the realization of dense interceptor

Myabtis package org.apache.ibatis.plugin.Interceptor interceptor interface requires us to achieve the following three methods

public interface Interceptor {

  //核心拦截逻辑
  Object intercept(Invocation invocation) throws Throwable;
  
  //拦截器链
  default Object plugin(Object target) {return Plugin.wrap(target, this);}

  //自定义配置文件操作
  default void setProperties(Properties properties) { }

}

Therefore, the official document of reference example, we participated in secret into a custom interceptor.

@Intercepts comment open interceptor, the actual type @ Signature annotation defines interceptors.

@Signature in

  • The type attribute specifies the current interceptor uses a StatementHandler, ResultSetHandler, ParameterHandler, Executor of
  • using a specific method property specifies a method of the above four types (class may view into the interior of its methods).
  • args attribute specifies the precompiled statement

Here we used ParameterHandler.setParamters () method, in intercepting Example mapper.xml paramsType (i.e., each attribute mapper paramsType statement contained in both the implementation of the interceptor, for instance to intercept paramsType process)

/**
 * 加密拦截器
 * 注意@Component注解一定要加上
 *
 * @author : tanzj
 * @date : 2020/1/19.
 */
@Slf4j
@Component
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
public class EncryptInterceptor implements Interceptor {

    private final EncryptDecryptUtil encryptUtil;

    @Autowired
    public EncryptInterceptor(EncryptDecryptUtil encryptUtil) {
        this.encryptUtil = encryptUtil;
    }

    @Override
   
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler 
        //若指定ResultSetHandler ,这里则能强转为ResultSetHandler
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        // 获取参数对像,即 mapper 中 paramsType 的实例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);
        //取出实例
        Object parameterObject = parameterField.get(parameterHandler);
        if (parameterObject != null) {
            Class<?> parameterObjectClass = parameterObject.getClass();
            //校验该实例的类是否被@SensitiveData所注解
            SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
            if (Objects.nonNull(sensitiveData)) {
                //取出当前当前类所有字段,传入加密方法
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                encryptUtil.encrypt(declaredFields, parameterObject);
            }
        }
        return invocation.proceed();
    }

    /**
     * 切记配置,否则当前拦截器不会加入拦截器链
     */
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    //自定义配置写入,没有自定义配置的可以直接置空此方法
    @Override
    public void setProperties(Properties properties) {
    }
}

This completes the custom encryption to intercept encrypted.

2.5 decryption defined interface and its implementation class

Decrypting the interface, wherein the result is an example of mapper.xml in resultType.

public interface DecryptUtil {

    /**
     * 解密
     *
     * @param result resultType的实例
     * @return T
     * @throws IllegalAccessException 字段不可访问异常
     */
     <T> T decrypt(T result) throws IllegalAccessException;
    
}

AES decryption tool decrypt the interface implementation class

public class AESDecrypt implements DecryptUtil {
    
    @Autowired
    AESUtil aesUtil;
    
    /**
     * 解密
     *
     * @param result resultType的实例
     * @return T
     * @throws IllegalAccessException 字段不可访问异常
     */
    @Override
    public <T> T decrypt(T result) throws IllegalAccessException {
        //取出resultType的类
        Class<?> resultClass = result.getClass();
        Field[] declaredFields = resultClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //取出所有被EncryptDecryptField注解的字段
            SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
            if (!Objects.isNull(sensitiveField)) {
                field.setAccessible(true);
                Object object = field.get(result);
                //只支持String的解密
                if (object instanceof String) {
                    String value = (String) object;
                    //对注解的字段进行逐一解密
                    field.set(result, aesUtil.decrypt(value));
                }
            }
        }
        return result;
    }
}

2.6 decryption parameters define interceptors

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

    @Autowired
    DecryptUtil aesDecrypt;

    @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) {
                    //逐一解密
                    aesDecrypt.decrypt(result);
                }
            }
        //基于selectOne
        } else {
            if (needToDecrypt(resultObject)) {
                aesDecrypt.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) {

    }
}

This completes the configuration work decrypt interceptors.

3, annotation requires encryption and decryption entity class field

In this case the mapper, designated paramType = User resultType = User can be realized from the business layer, based interceptors add mybatis decryption operation.

Released nine original articles · won praise 23 · views 40000 +

Guess you like

Origin blog.csdn.net/bbcckkl/article/details/104069487