On the implementation of mobile phone number, ID number encryption and decryption solution

On the implementation of mobile phone number, ID number encryption and decryption solution

Require:

1.不入侵任何业务逻辑
2.可以扩展解密解密字段
3.可以扩展加密解密算法

environment:

jdk1.8、springMVC

solution:

基于自定义注解和mybatis自定义拦截器实现

Implementation:

自定义注解 @SensitiveData ,@SensitiveField

主要作用是:可以扩展任何想要加密的字段,只要判断加密的实体类上是否有这两个注解即可
/**
* 注解敏感信息类的注解
*/
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {}


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

Define the encryption and decryption interface for unified processing

package com.gisquest.platform.annotation;
import java.lang.reflect.Field;
public interface EncryptUtil {
      /*declaredFields:传入加密注解修改的字段
      * paramsObject:传入对应xml中paramterType对应的实例对象
      */
	<T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException;
}

package com.gisquest.platform.annotation;
public interface DecryptUtil {
      /*
      * result:传入对应xml中resultType对应的实例对象
      */
	<T> T decrypt(T result) throws IllegalAccessException;
}

Define the implementation class of the encryption and decryption interface for unified processing

@Component
public class EncryptProcessor implements EncryptUtil{
	
@Value(value="${encrypt.algorithmType}")	
private String algorithmType; 
@Override
public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
	for (Field field : declaredFields) {
		//取出所有被注解的字段
		SensitiveField sensitiveField = field.getDeclaredAnnotation(SensitiveField.class);
		if (!Objects.isNull(sensitiveField)) {
			field.setAccessible(true);
			//获取该字段对应的value值
			Object object = field.get(paramsObject);
		    //暂时只实现String类型的加密
		    if (object instanceof String) {
			   String value = (String) object;
			   if(!StringUtils.isEmpty(algorithmType)) {
				   field.set(paramsObject, CommonAlgorithmUtils.encrypt(algorithmType,value)); 
			   }
             }
		  }
        }
	   return paramsObject;
   }
}

Define the implementation class of the decryption interface

@Component
public class DecryptProcessor implements DecryptUtil {

	@Value(value="${decrypt.algorithmType}")	
	private String algorithmType;
		
	@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;
				  if(!StringUtils.isEmpty(algorithmType)) {
			        //对注解的字段进行逐一解密
			        field.set(result, CommonAlgorithmUtils.decrypt(algorithmType,value));
				  }
	       	   }
		   }
		}
		return result;
	}
}

Interceptor when defining encryption

package com.gisquest.platform.conf;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.core.annotation.AnnotationUtils;

import com.gisquest.platform.annotation.EncryptUtil;
import com.gisquest.platform.annotation.SensitiveData;


@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class) })
public class EncryptInterceptor implements Interceptor {
	private  EncryptUtil encryptUtil;
	public EncryptInterceptor(EncryptUtil encryptUtil) {
		this.encryptUtil = encryptUtil;
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		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) {
	}
}

Interceptor when defining decryption

package com.gisquest.platform.conf;

import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;
import com.gisquest.platform.annotation.DecryptUtil;
import com.gisquest.platform.annotation.SensitiveData;

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

	private final DecryptUtil decryptUtil;

	DecryptInterceptor(DecryptUtil decryptUtil) {
		this.decryptUtil = decryptUtil;
	}

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

@Intercepts: Identify this class as an interceptor

@Signature: Identify the interface to be intercepted, the interceptor that mybatis can extend

type: Identify the specific interface to be intercepted (ResultSetHandler.class,)

method: Identify the method in the interface to be intercepted (handleResultSets)

args: Identify the specific input parameters to intercept the interface method

Inject custom mybatis interceptors using configuration classes

@Configuration
public class IbatisInterceptorConfig{
	
	@Value(value = "${encrypt.enable}")
	public boolean isEnableEncrypt;
	
	@Value(value = "${decrypt.enable}")
	public boolean isEnableDecrypt;
	
	@Autowired
	EncryptProcessor  encryptProcessor;
	
	@Autowired
    DecryptProcessor  decryptProcessor;
	
	@Autowired
	ProcessEngineConfigurationImpl  processEngineFactoryBean;
	
	@PostConstruct
	public void addInterceptor() {
	    //获取项目中的mybatis的SqlSessionFactory
		SqlSessionFactory sqlSessionFactory=processEngineFactoryBean.getSqlSessionFactory();
		
		if(isEnableEncrypt) {
			sqlSessionFactory.getConfiguration().addInterceptor(new EncryptInterceptor(encryptProcessor));
		}
		if(isEnableDecrypt) {
			sqlSessionFactory.getConfiguration().addInterceptor(new DecryptInterceptor(decryptProcessor));
		}
	}
}

configuration file:

The configuration in application.properties is as follows

encrypt.enable=true
encrypt.algorithmType=AES
decrypt.enable=true
decrypt.algorithmType=AES

Algorithm Unified Processing Tool

package com.gisquest.platform.common.utils;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import com.gisquest.platform.common.config.Global;

public class CommonAlgorithmUtils {
	
	public static String encrypt(String algorithm,String content){
		String result=StringUtils.EMPTY;
		if(StringUtils.isEmpty(algorithm)) {
			result=content;
			return result;
		}
		if(!StringUtils.isEmpty(algorithm)) {
		  switch(algorithm){
		       case   "AES":
			       result = AESUtils.encrypt(content, Global.getConfig("login.encryptKey"));
		           break;
		       case   "RSA":
		    	   //使用时具体自己补充
		    	   PublicKey publicKey=null;
		    	   Cipher cipher=null;
				try {
					result=RsaUtils.encrypt(content, publicKey, cipher);
					 break;
				} catch (InvalidKeyException | UnsupportedEncodingException | BadPaddingException
						| IllegalBlockSizeException e) {
					e.printStackTrace();
				}
		       case   "SM4":
		    	   result=SM4Utils.encryptEcb(content, SM4Utils.SECURE_LOG_KEY); 
		    	   break;
		       default:
		    	   result = AESUtils.encrypt(content, Global.getConfig("login.encryptKey"));
			}
		}
		return result;
	}
	
	
	
	
	public static String decrypt(String algorithm,String content){
		String result=StringUtils.EMPTY;
		if(StringUtils.isEmpty(algorithm)) {
			result=content;
			return result;
		}
		if(!StringUtils.isEmpty(algorithm)) {
		  switch(algorithm){
		       case   "AES":
			       result = AESUtils.decrypt(content, Global.getConfig("login.encryptKey"));
		           break;
		       case   "RSA":
		    	   //使用时具体自己补充
		    	    PrivateKey privateKey=null;
					try {
						result=RsaUtils.decrypt(content, privateKey);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					 break;
		       case   "SM4":
		    	   //使用时具体自己补充
		    	   result=SM4Utils.decryptEcb(content, SM4Utils.SECURE_LOG_KEY); 
		    	   break;
		       default:
		    	   result = AESUtils.decrypt(content, Global.getConfig("login.encryptKey"));
			}
		}
		return result;
	}
}

import com.gisquest.platform.annotation.SensitiveData;
import com.gisquest.platform.annotation.SensitiveField;

@SensitiveData
public class UserEntity  {
	private String id; 
	private String username;
	@SensitiveField
	private String staffName; // 用户姓名
	//省略setter getter
}

Guess you like

Origin blog.csdn.net/qq_38423256/article/details/129443635