1.隐位处理

使用方法

返回对象实体中增加注解

@Sensitive(strategy = SensitiveStrategy.PHONE)
private String phone;

请求返回方式如下:返回类型必须为pojo对象,例如【UserInfoVO】

@PostMapping("/queryUser")
@ApiOperation(value = "查询", notes = "4")
public PagerListResp<UserInfoVO> queryUser(HttpServletRequest request) {UserInfoReq req = RequestBodyUtils.getRequestBody(request, UserInfoReq.class);PagerListResp<UserInfoVO> resp = new PagerListResp<>();try {resp = userService.queryUser(req);} catch (Exception e) {resp.setRetCode(ListResp.RETURN_FAILURE);resp.setRetMsg("系统异常, 请稍后再试!");LOGGER.error("UserInfoController.queryUser exception:", e);}return resp;
}

实例方案

新增注解Sensitive


import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义jackson注解,标注在属性上** @author java*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
//这个注解用来标记Jackson复合注解,当你使用多个Jackson注解组合成一个自定义注解时会用到它
@JacksonAnnotationsInside
//指定使用自定义的序列化器
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {//脱敏策略SensitiveStrategy strategy();/*** 前置不需要打码的长度*/int prefixNoMaskLen() default 1;/*** 后置不需要打码的长度*/int suffixNoMaskLen() default 1;/*** 用什么打码*/String symbol() default "*";
}

序列化注解自定义实现【SensitiveJsonSerializer】


import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import org.apache.commons.lang3.StringUtils;import java.io.IOException;
import java.util.Map;
import java.util.Objects;/*** 序列化注解自定义实现* JsonSerializer<String>:指定String 类型,serialize()方法用于将修改后的数据载入*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {private SensitiveStrategy strategy;// 前几位不脱敏private Integer prefixNoMaskLen;// 最后几位不脱敏private Integer suffixNoMaskLen;// 用什么打码private String symbol;@Overridepublic void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {Map<String, String> map = LoginUtil.getCurrentLogin();if (StringUtils.isNotBlank(value) && null != strategy) {switch (strategy) {case USERNAME:value = DesensitizationUtil.custNameDesensitization(value);break;case REAL_NAME:value = DesensitizationUtil.custNameDesensitization(value);break;case ID_CARD:value = DesensitizationUtil.idCardDesensitization(value);break;case PHONE:value = DesensitizationUtil.mobilePhoneDesensitization(value);break;case EMAIL:value = DesensitizationUtil.emailDesensitization(value);break;case ADDRESS:value = DesensitizationUtil.addressDesensitization(value);break;case BANK_CARD:value = DesensitizationUtil.acctNoDesensitization(value);break;case CAR_NUMBER:value = DesensitizationUtil.carNumberDesensitization(value);break;default:value = value;}}jsonGenerator.writeString(value);}/*** 获取属性上的注解属性*/@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {Sensitive annotation = property.getAnnotation(Sensitive.class);if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {this.strategy = annotation.strategy();return this;}return prov.findValueSerializer(property.getType(), property);}
}

隐位策略SensitiveStrategy

public enum SensitiveStrategy {/*** 用户名*/USERNAME,/*** 身份证*/ID_CARD,/*** 手机号*/PHONE,/*** 银行卡*/BANK_CARD,/*** 真实姓名*/REAL_NAME,EMAIL,/*** 地址*/ADDRESS,/*** 车牌号*/CAR_NUMBER;
}

返回前端同时返回脱敏数据与加密数据

脱敏数据增加XXXEncrypt字段,例如idCardEncrypt

同时修改setXXX方法,将数据库查询出的数据,赋值给XXXEncrypt字段

@Sensitive(strategy = SensitiveStrategy.ID_CARD)
@TableField(typeHandler = Sm4TypeHandler.class)
private String idCard;@TableField(exist = false)
private String idCardEncrypt;public String getIdCard() {return idCard;}public void setIdCard(String idCard) {this.idCard = idCard;this.idCardEncrypt = idCard;}public String getIdCardEncrypt() {return idCardEncrypt;}public void setIdCardEncrypt(String idCardEncrypt) {this.idCardEncrypt = idCardEncrypt;}

2.存储加解密,使用mybatis-plus,加密算法国密SM4

存储加解密,使用注解@TableField

使用方式,查询与保存时需要使用对象,不可以使用map

@TableField(typeHandler = Sm4TypeHandler.class)
private String phoneNo;

实现方案:

定义加密处理类Sm4TypeHandler


import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class Sm4TypeHandler extends BaseTypeHandler<String> {private static final Logger log = LoggerFactory.getLogger(Sm4TypeHandler.class);/*** 16位key*/private static final String SM4_KEY = "Zwrx0803!@";/*** 非空字段加密 - 入库* @param preparedStatement* @param i* @param parameter* @param jdbcType*/@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, String parameter, JdbcType jdbcType) throws SQLException {//不处理空字符串if(StringUtils.isBlank(parameter)){return;}try {preparedStatement.setString(i, Sm4Util.encryptHex(parameter));} catch (Exception e) {log.error("typeHandler加密异常:", e);}}/*** 非空字段解密 - 出库* @param resultSet* @param columnName* @return* @throws SQLException*/@Overridepublic String getNullableResult(ResultSet resultSet, String columnName) throws SQLException {String col = resultSet.getString(columnName);//不处理空字符串if(StringUtils.isBlank(col)){return col;}try {String plain = Sm4Util.decryptStr(col);log.info("数据:{},解密{}",col,plain);return plain;} catch (Exception e) {log.error("数据非sms解密",e);}return col;}/*** 可空字段加密* @param resultSet* @param columnIndex* @return* @throws SQLException*/@Overridepublic String getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {return resultSet.getString(columnIndex);}/*** 可空字段解密* @param callableStatement* @param columnIndex* @return* @throws SQLException*/@Overridepublic String getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {return callableStatement.getString(columnIndex);}
}

定义数据库加密字段解密拦截器Sm4DecryptFieldInterceptor

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.TableField;import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.*;@Intercepts({// type 指定代理对象,method 指定代理方法,args 指定type代理类中method方法的参数@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),})
@Component
public class Sm4DecryptFieldInterceptor implements Interceptor {private static final Logger log = LoggerFactory.getLogger(Sm4DecryptFieldInterceptor.class);static int MAPPED_STATEMENT_INDEX = 0;static int PARAMETER_INDEX = 1;static int ROWBOUNDS_INDEX = 2;static int RESULT_HANDLER_INDEX = 3;static String ENCRYPTFIELD = "1";static String DECRYPTFIELD = "2";@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];String sqlId = mappedStatement.getId();log.info("==> intercept  SQL_ID: [{}]", sqlId);Object proceed = invocation.proceed();if (sqlId.contains("selectCount")) {log.warn("==> SQL 语句类型是[selectCount] 直接返回结果");return proceed;}Object parameter = null;if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[PARAMETER_INDEX];}// parameter = encryptParam(parameter, invocation);BoundSql boundSql = mappedStatement.getBoundSql(parameter);Configuration configuration = mappedStatement.getConfiguration();String sql = genSql(configuration, boundSql);if (sql.contains("count") || sql.contains("COUNT") || sql.contains("Count")) {log.warn("==> SQL 语句包含[count|COUNT|Count] 直接返回结果");return proceed;}Object returnValue = invocation.proceed();log.info(sqlId + "未解密结果集:" + JSONObject.toJSONString(returnValue));returnValue = decryptReslut(returnValue, invocation);log.info(sqlId + "解密后结果集:" + JSONObject.toJSONString((returnValue)));log.info("EncryptDaoInterceptor.intercept执行结束==> ");return returnValue;}/*** 解密结果集** @param @param  returnValue* @param @param  invocation* @param @return* @return Object* @throws*/public Object decryptReslut(Object returnValue, Invocation invocation) {MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];if (returnValue != null) {if (returnValue instanceof ArrayList<?>) {List<?> list = (ArrayList<?>) returnValue;List<Object> newList = new ArrayList<Object>();if (1 <= list.size()) {for (Object object : list) {Object obj = decrypt(object);newList.add(obj);}returnValue = newList;}} else if (returnValue instanceof Map) {String[] fields = getEncryFieldList(statement, DECRYPTFIELD);if (fields != null) {returnValue = getDecryptMapValue(returnValue, fields);}} else {returnValue = decrypt(returnValue);}}return returnValue;}/*** 对含注解字段解密** @param t* @param <T>*/public static <T> T decrypt(T t) {if (!(t instanceof Map)) {List<Field> fieldList = new ArrayList();Field[] declaredFields = t.getClass().getDeclaredFields();CollectionUtils.addAll(fieldList,declaredFields);if( t.getClass().getSuperclass()!=null){Field[] superDeclaredFields =  t.getClass().getSuperclass().getDeclaredFields();CollectionUtils.addAll(fieldList,superDeclaredFields);}try {if (fieldList != null && fieldList.size() > 0) {for (Field field : fieldList) {if (field.isAnnotationPresent(TableField.class) && field.getType().toString().endsWith("String")) {TableField  tableField = field.getAnnotation(TableField.class);Class typeHandlerClass = tableField.typeHandler();if(typeHandlerClass ==  Sm4TypeHandler.class){field.setAccessible(true);String fieldValue = (String) field.get(t);if (StringUtils.isNotEmpty(fieldValue)) {field.set(t, Sm4Util.decryptStr(fieldValue));}}}}}} catch (Exception e) {throw new RuntimeException(e);}}return t;}/**** 针对不同的参数类型进行加密* @param @param parameter* @param @param invocation* @param @return* @return Object* @throws**/public Object encryptParam(Object parameter, Invocation invocation) {MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];try {if (parameter instanceof String) {if (isEncryptStr(statement)) {parameter = Sm4Util.encryptHex(parameter.toString());}} else if (parameter instanceof Map) {String[] fields = getEncryFieldList(statement, ENCRYPTFIELD);if (fields != null) {parameter = getEncryptMapValue(parameter, fields);}} else {parameter = encrypt(parameter);}} catch (ClassNotFoundException e) {log.error("EncryptDaoInterceptor.encryptParam方法异常==> " , e);}return parameter;}/*** 对象t注解字段加密** @param t* @param <T>* @return*/public static <T> T encrypt(T t) {if (isEncryptAndDecrypt(t)) {Field[] declaredFields = t.getClass().getDeclaredFields();try {if (declaredFields != null && declaredFields.length > 0) {for (Field field : declaredFields) {if (field.isAnnotationPresent(EncryptField.class) && field.getType().toString().endsWith("String")) {field.setAccessible(true);String fieldValue = (String) field.get(t);if (StringUtils.isNotEmpty(fieldValue)) {field.set(t, Sm4Util.encryptHex(fieldValue));}field.setAccessible(false);}}}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}return t;}public static Object getEncryptMapValue(Object parameter, String[] fields) {Map<String,Object> map = null;try {map = (Map<String, Object>) parameter;} catch (Exception e) {return parameter;}for (String field : fields) {if (!map.containsKey(field)) {continue;}if (map.get(field) instanceof String) {String value = String.valueOf(map.get(field));if (Strings.EMPTY.equals(value)) {continue;}for (Map.Entry<String, Object> entry : map.entrySet()) {if (value.equals(entry.getValue())) {map.put(entry.getKey(), Sm4Util.encryptHex(value));}}}}return map;}/*** 判断字符串是否需要加密** @param @param  mappedStatement* @param @return* @return boolean* @throws*/private boolean isEncryptStr(MappedStatement mappedStatement) throws ClassNotFoundException {boolean reslut = false;try {Method m = getDaoTargetMethod(mappedStatement);m.setAccessible(true);Annotation[][] parameterAnnotations = m.getParameterAnnotations();if (parameterAnnotations != null && parameterAnnotations.length > 0) {for (Annotation[] parameterAnnotation : parameterAnnotations) {for (Annotation annotation : parameterAnnotation) {if (annotation instanceof EncryptField) {reslut = true;}}}}} catch (SecurityException e) {log.error("EncryptDaoInterceptor.isEncryptStr异常:==> " , e);reslut = false;}return reslut;}/*** 获取参数map中需要加密字段** @param statement* @param type* @return List<String>* @throws*/private String[] getEncryFieldList(MappedStatement statement, String type) {String[] strArry = null;Method method = getDaoTargetMethod(statement);if(method==null){strArry=  new String[] {"phone"};return strArry;}// strArry=  new String[] {"phone"};Annotation annotation = method.getAnnotation(EncryptMethod.class);if (annotation != null) {if (type.equals(ENCRYPTFIELD)) {strArry = ((EncryptMethod) annotation).encrypt();} else if (type.equals(DECRYPTFIELD)) {strArry = ((EncryptMethod) annotation).decrypt();} else {strArry = null;}}return strArry;}public static Object getDecryptMapValue(Object returnValue, String[] fields){return null;}/*** 获取Dao层接口方法** @param @return* @return Method* @throws*/private Method getDaoTargetMethod(MappedStatement mappedStatement) {Method method = null;try {String namespace = mappedStatement.getId();String className = namespace.substring(0, namespace.lastIndexOf("."));String methedName = namespace.substring(namespace.lastIndexOf(".") + 1, namespace.length());Method[] ms = Class.forName(className).getMethods();for (Method m : ms) {if (m.getName().equals(methedName)) {method = m;break;}}} catch (SecurityException e) {log.error("EncryptDaoInterceptor.getDaoTargetMethod方法异常==> " ,e);return method;} catch (ClassNotFoundException e) {log.error("EncryptDaoInterceptor.getDaoTargetMethod方法异常==> " ,e);return method;}return method;}/*** 判断是否需要加密解密的类** @param @param  t* @param @return* @return Boolean* @throws*/public static <T> Boolean isEncryptAndDecrypt(T t) {return true;}private String genSql(Configuration configuration, BoundSql boundSql) {Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();String sql = boundSql.getSql().replaceAll("[\\s]+", " ");if (parameterMappings.size() > 0 && null != parameterObject) {TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {sql = sql.replaceFirst("\\?", getParameterVal(parameterObject));} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);for (ParameterMapping parameterMapping : parameterMappings) {String propertyName = parameterMapping.getProperty();if (metaObject.hasGetter(propertyName)) {Object value = metaObject.getValue(propertyName);if ("phone".equalsIgnoreCase(propertyName)) {log.info("==> genSql before [{}]: [{}]", propertyName, value);value = Sm4Util.encryptHex(value.toString());log.info("==> genSql after [{}]: [{}]", propertyName, value);metaObject.setValue(propertyName, value);}sql = sql.replaceFirst("\\?", getParameterVal(value));} else if (boundSql.hasAdditionalParameter(propertyName)) {Object value = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?", getParameterVal(value));}}}}return sql;}private String getParameterVal(Object obj) {String val;if (obj instanceof String) {val = "'" + obj.toString() + "'";} else if (obj instanceof Date) {DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);val = "'" + dateFormat.format(obj) + "'";} else {if (null != obj) {val = obj.toString();} else {val = "";}}return val;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

3.查询数据库加密字段,利用aop拦截处理

3.1使用方法注解@EncryptMethod

使用样例,encrypt表示需加密的请求参数,用于进行数据库查询

@EncryptMethod(encrypt = {"phoneNo","idcard"})
public PagerListResp<UserInfoVO> queryUser(UserInfoReq req) {ProcessPermission.execute(req);if (StringUtils.isNotBlank(req.getRoleId())) {List<String> roleIdList = Arrays.asList(req.getRoleId().split(","));req.setRoleIdList(roleIdList);}}

3.2实现方式

3.2.1EncryptMethod注解


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {String[] encrypt() default "";String[] decrypt() default "";
}

3.2.2aop拦截类Sm4DecryptFieldInterceptor

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.TableField;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.*;@Intercepts({// type 指定代理对象,method 指定代理方法,args 指定type代理类中method方法的参数@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),})
@Component
public class Sm4DecryptFieldInterceptor implements Interceptor {private static final Logger log = LoggerFactory.getLogger(Sm4DecryptFieldInterceptor.class);static int MAPPED_STATEMENT_INDEX = 0;static int PARAMETER_INDEX = 1;static int ROWBOUNDS_INDEX = 2;static int RESULT_HANDLER_INDEX = 3;static String ENCRYPTFIELD = "1";static String DECRYPTFIELD = "2";@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];String sqlId = mappedStatement.getId();log.info("==> intercept  SQL_ID: [{}]", sqlId);Object proceed = invocation.proceed();if (sqlId.contains("selectCount")) {log.warn("==> SQL 语句类型是[selectCount] 直接返回结果");return proceed;}Object parameter = null;if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[PARAMETER_INDEX];}// parameter = encryptParam(parameter, invocation);BoundSql boundSql = mappedStatement.getBoundSql(parameter);Configuration configuration = mappedStatement.getConfiguration();String sql = genSql(configuration, boundSql);if (sql.contains("count") || sql.contains("COUNT") || sql.contains("Count")) {log.warn("==> SQL 语句包含[count|COUNT|Count] 直接返回结果");return proceed;}Object returnValue = invocation.proceed();log.info(sqlId + "未解密结果集:" + JSONObject.toJSONString(returnValue));returnValue = decryptReslut(returnValue, invocation);log.info(sqlId + "解密后结果集:" + JSONObject.toJSONString((returnValue)));log.info("EncryptDaoInterceptor.intercept执行结束==> ");return returnValue;}/*** 解密结果集** @param @param  returnValue* @param @param  invocation* @param @return* @return Object* @throws*/public Object decryptReslut(Object returnValue, Invocation invocation) {MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];if (returnValue != null) {if (returnValue instanceof ArrayList<?>) {List<?> list = (ArrayList<?>) returnValue;List<Object> newList = new ArrayList<Object>();if (1 <= list.size()) {for (Object object : list) {Object obj = decrypt(object);newList.add(obj);}returnValue = newList;}} else if (returnValue instanceof Map) {String[] fields = getEncryFieldList(statement, DECRYPTFIELD);if (fields != null) {returnValue = getDecryptMapValue(returnValue, fields);}} else {returnValue = decrypt(returnValue);}}return returnValue;}/*** 对含注解字段解密** @param t* @param <T>*/public static <T> T decrypt(T t) {if (!(t instanceof Map)) {List<Field> fieldList = new ArrayList();Field[] declaredFields = t.getClass().getDeclaredFields();CollectionUtils.addAll(fieldList,declaredFields);if( t.getClass().getSuperclass()!=null){Field[] superDeclaredFields =  t.getClass().getSuperclass().getDeclaredFields();CollectionUtils.addAll(fieldList,superDeclaredFields);}try {if (fieldList != null && fieldList.size() > 0) {for (Field field : fieldList) {if (field.isAnnotationPresent(TableField.class) && field.getType().toString().endsWith("String")) {TableField  tableField = field.getAnnotation(TableField.class);Class typeHandlerClass = tableField.typeHandler();if(typeHandlerClass ==  Sm4TypeHandler.class){field.setAccessible(true);String fieldValue = (String) field.get(t);if (StringUtils.isNotEmpty(fieldValue)) {field.set(t, Sm4Util.decryptStr(fieldValue));}}}}}} catch (Exception e) {throw new RuntimeException(e);}}return t;}/**** 针对不同的参数类型进行加密* @param @param parameter* @param @param invocation* @param @return* @return Object* @throws**/public Object encryptParam(Object parameter, Invocation invocation) {MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];try {if (parameter instanceof String) {if (isEncryptStr(statement)) {parameter = Sm4Util.encryptHex(parameter.toString());}} else if (parameter instanceof Map) {String[] fields = getEncryFieldList(statement, ENCRYPTFIELD);if (fields != null) {parameter = getEncryptMapValue(parameter, fields);}} else {parameter = encrypt(parameter);}} catch (ClassNotFoundException e) {log.error("EncryptDaoInterceptor.encryptParam方法异常==> " , e);}return parameter;}/*** 对象t注解字段加密** @param t* @param <T>* @return*/public static <T> T encrypt(T t) {if (isEncryptAndDecrypt(t)) {Field[] declaredFields = t.getClass().getDeclaredFields();try {if (declaredFields != null && declaredFields.length > 0) {for (Field field : declaredFields) {if (field.isAnnotationPresent(EncryptField.class) && field.getType().toString().endsWith("String")) {field.setAccessible(true);String fieldValue = (String) field.get(t);if (StringUtils.isNotEmpty(fieldValue)) {field.set(t, Sm4Util.encryptHex(fieldValue));}field.setAccessible(false);}}}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}return t;}public static Object getEncryptMapValue(Object parameter, String[] fields) {Map<String,Object> map = null;try {map = (Map<String, Object>) parameter;} catch (Exception e) {return parameter;}for (String field : fields) {if (!map.containsKey(field)) {continue;}if (map.get(field) instanceof String) {String value = String.valueOf(map.get(field));if (Strings.EMPTY.equals(value)) {continue;}for (Map.Entry<String, Object> entry : map.entrySet()) {if (value.equals(entry.getValue())) {map.put(entry.getKey(), Sm4Util.encryptHex(value));}}}}return map;}/*** 判断字符串是否需要加密** @param @param  mappedStatement* @param @return* @return boolean* @throws*/private boolean isEncryptStr(MappedStatement mappedStatement) throws ClassNotFoundException {boolean reslut = false;try {Method m = getDaoTargetMethod(mappedStatement);m.setAccessible(true);Annotation[][] parameterAnnotations = m.getParameterAnnotations();if (parameterAnnotations != null && parameterAnnotations.length > 0) {for (Annotation[] parameterAnnotation : parameterAnnotations) {for (Annotation annotation : parameterAnnotation) {if (annotation instanceof EncryptField) {reslut = true;}}}}} catch (SecurityException e) {log.error("EncryptDaoInterceptor.isEncryptStr异常:==> " , e);reslut = false;}return reslut;}/*** 获取参数map中需要加密字段** @param statement* @param type* @return List<String>* @throws*/private String[] getEncryFieldList(MappedStatement statement, String type) {String[] strArry = null;Method method = getDaoTargetMethod(statement);if(method==null){strArry=  new String[] {"phone"};return strArry;}// strArry=  new String[] {"phone"};Annotation annotation = method.getAnnotation(EncryptMethod.class);if (annotation != null) {if (type.equals(ENCRYPTFIELD)) {strArry = ((EncryptMethod) annotation).encrypt();} else if (type.equals(DECRYPTFIELD)) {strArry = ((EncryptMethod) annotation).decrypt();} else {strArry = null;}}return strArry;}public static Object getDecryptMapValue(Object returnValue, String[] fields){return null;}/*** 获取Dao层接口方法** @param @return* @return Method* @throws*/private Method getDaoTargetMethod(MappedStatement mappedStatement) {Method method = null;try {String namespace = mappedStatement.getId();String className = namespace.substring(0, namespace.lastIndexOf("."));String methedName = namespace.substring(namespace.lastIndexOf(".") + 1, namespace.length());Method[] ms = Class.forName(className).getMethods();for (Method m : ms) {if (m.getName().equals(methedName)) {method = m;break;}}} catch (SecurityException e) {log.error("EncryptDaoInterceptor.getDaoTargetMethod方法异常==> " ,e);return method;} catch (ClassNotFoundException e) {log.error("EncryptDaoInterceptor.getDaoTargetMethod方法异常==> " ,e);return method;}return method;}/*** 判断是否需要加密解密的类** @param @param  t* @param @return* @return Boolean* @throws*/public static <T> Boolean isEncryptAndDecrypt(T t) {return true;}private String genSql(Configuration configuration, BoundSql boundSql) {Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();String sql = boundSql.getSql().replaceAll("[\\s]+", " ");if (parameterMappings.size() > 0 && null != parameterObject) {TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {sql = sql.replaceFirst("\\?", getParameterVal(parameterObject));} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);for (ParameterMapping parameterMapping : parameterMappings) {String propertyName = parameterMapping.getProperty();if (metaObject.hasGetter(propertyName)) {Object value = metaObject.getValue(propertyName);if ("phone".equalsIgnoreCase(propertyName)) {log.info("==> genSql before [{}]: [{}]", propertyName, value);value = Sm4Util.encryptHex(value.toString());log.info("==> genSql after [{}]: [{}]", propertyName, value);metaObject.setValue(propertyName, value);}sql = sql.replaceFirst("\\?", getParameterVal(value));} else if (boundSql.hasAdditionalParameter(propertyName)) {Object value = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?", getParameterVal(value));}}}}return sql;}private String getParameterVal(Object obj) {String val;if (obj instanceof String) {val = "'" + obj.toString() + "'";} else if (obj instanceof Date) {DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);val = "'" + dateFormat.format(obj) + "'";} else {if (null != obj) {val = obj.toString();} else {val = "";}}return val;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

加密工具类Sm4Util

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;public class Sm4Util {private static final Logger log = LoggerFactory.getLogger(Sm4Util.class);/*** 16位key*/private static final String SM4_KEY = "Zwrx0803Zwrx08!@";/*** 加密* @param parameter* @return*/public static String encryptHex(String parameter){if(StringUtils.isBlank(parameter)){return parameter;}try {SM4 sm4 = SmUtil.sm4(SM4_KEY.getBytes(StandardCharsets.UTF_8));String encrypt = sm4.encryptHex(parameter,StandardCharsets.UTF_8);log.info("数据:{},加密{}",parameter,encrypt);return encrypt;}catch (Exception e) {log.error("数据sm4加密失败",e);}return parameter;}/*** 解密* @param val* @return*/public static String decryptStr(String val){if(StringUtils.isBlank(val)){return val;}try {SM4 sm4 = SmUtil.sm4(SM4_KEY.getBytes(StandardCharsets.UTF_8));String plain = sm4.decryptStr(val,StandardCharsets.UTF_8);log.info("数据:{},解密{}",val,plain);return plain;} catch (Exception e) {log.error("数据非sms加密");}return val;}public static void main(String[] args) {log.info(encryptHex("15821306933"));}
}

敏感词加解密与隐位处理相关推荐

  1. delphi7aes加密解密与java互转_惊呆了!不改一行Java代码竟然就能轻松解决敏感信息加解密|原创

    前言 出于安全考虑,现需要将数据库的中敏感信息加密存储到数据库中,但是正常业务交互还是需要使用明文数据,所以查询返回我们还需要经过相应的解密才能返回给调用方. ❝ ps:日常开发中,我们要有一定的安全 ...

  2. 解决 IDEA 调用其他类的时候自动加上包路径和类名的情况_惊呆了!不改一行 Java 代码竟然就能轻松解决敏感信息加解密...

    前言 出于安全考虑,现需要将数据库的中敏感信息加密存储到数据库中,但是正常业务交互还是需要使用明文数据,所以查询返回我们还需要经过相应的解密才能返回给调用方. ❝ ps:日常开发中,我们要有一定的安全 ...

  3. 惊呆了!不改一行 Java 代码竟然就能轻松解决敏感信息加解密

    前言 出于安全考虑,现需要将数据库的中敏感信息加密存储到数据库中,但是正常业务交互还是需要使用明文数据,所以查询返回我们还需要经过相应的解密才能返回给调用方. ❝ ps:日常开发中,我们要有一定的安全 ...

  4. java请求url加密_SpringCloud-Config通过Java访问URL对敏感词加密解密

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...

  5. 在SpringBoot项目中,自定义注解+拦截器优雅的实现敏感数据的加解密!

    在实际生产项目中,经常需要对如身份证信息.手机号.真实姓名等的敏感数据进行加密数据库存储,但在业务代码中对敏感信息进行手动加解密则十分不优雅,甚至会存在错加密.漏加密.业务人员需要知道实际的加密规则等 ...

  6. 很全的敏感词匹配系统的设计与实践

    作者:vivo互联网服务器团队-Liang Kangwu 一.前言 谛听系统是vivo的内容审核平台,保障了vivo各互联网产品持续健康的发展.谛听支持审核多种内容类型,但日常主要审核的内容是文本,下 ...

  7. 如何设计一个敏感词匹配系统?

    ▲ 点击上方"分布式实验室"关注公众号 回复"1"抽取纸质技术书 谛听系统是vivo的内容审核平台,保障了vivo各互联网产品持续健康的发展.谛听支持审核多种内 ...

  8. java 敏感词检测

    在网上看到好多的敏感词检测,发现都是在推荐某某算法,但是敏感词全是利用文本去存放.在项目中不能很好的进行维护和管理(个人看法). 本文的敏感词的检测方式还是DFA算法检测,不过敏感词存放地址放入了Re ...

  9. 【Lilishop商城】No3-2.模块详细设计,系统设置(系统配置、行政区划、物流公司、滑块验证码图片、敏感词过滤)的详细设计

     仅涉及后端,全部目录看顶部专栏,代码.文档.接口路径在: [Lilishop商城]记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑,其中重点包括接 ...

最新文章

  1. Geo-CNN的三维点云
  2. 华为5G手机芯片被唱衰:美研究机构拆解6款量产机,不谈能力对标高通骁龙X50...
  3. 存储过程 psal emp.sal%type是什么意思
  4. 如何快速分辨一个男人是不是程序员
  5. Express请求处理-静态资源的处理
  6. 2013年第四届蓝桥杯C/C++ A组国赛 —— 第二题:骰子迷题
  7. UICollectionViewController
  8. SpringBoot实现JWT保护前后端分离RESTful API
  9. 没学过JavaScript也能看懂的闭包解释
  10. js获取文件的后缀名
  11. 一条查询SQL的执行流程
  12. java计算机毕业设计化妆品销售网站MyBatis+系统+LW文档+源码+调试部署
  13. 使用SoftEther 上免费校园网(ipv6)
  14. 明星也爱字体——赵丽颖秀气字体:(江湖少女两版)蓄势待发
  15. STM32驱动TM1616程序加原理图
  16. r语言r-shiny_如何使用R Shiny进行EDA和预测
  17. bcdedit添加linux引导,强大的BCDEdit工具-启动项等相关问题-设置默认开机启动项
  18. java socket 打印机_socket.BeginReceive()使打印机无响应
  19. 把执行结果转成json对象报错_关于JSON转换成对象 报错LinkedHashMap不能直接转成对象...
  20. matplotlib、numpy、pandas知识

热门文章

  1. 朴素贝叶斯、精确率与召回率、交叉验证
  2. 深度学习--大黄蜂预测
  3. 设计模式(Java)—Interpreter模式
  4. Xmate pro3机械臂PC连接不上的问题
  5. datastage java_datastage server job之java调用datastage job
  6. ReadFile功能
  7. 网页打开慢的解决方法
  8. Python - 通过whl文件安装第三方库
  9. AndroidStudio+Genymotion(附破解方法)安卓开发环境搭建
  10. c# xor运算_C#程序使用XOR运算符交换数字