All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ideaaedi.mybatis.data.security.support.EncryptInfoHolder Maven / Gradle / Ivy

There is a newer version: 1.4.3-mp3.5.1
Show newest version
package com.ideaaedi.mybatis.data.security.support;

import com.ideaaedi.mybatis.data.security.annotation.Encrypt;
import com.ideaaedi.mybatis.data.security.enums.TypeEnum;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.ideaaedi.mybatis.data.security.enums.TypeEnum.ARRAY;
import static com.ideaaedi.mybatis.data.security.enums.TypeEnum.COLLECTION;
import static com.ideaaedi.mybatis.data.security.enums.TypeEnum.CUSTOM_BEAN;
import static com.ideaaedi.mybatis.data.security.enums.TypeEnum.MAP;
import static com.ideaaedi.mybatis.data.security.enums.TypeEnum.PRIMITIVE_OR_WRAPPER;
import static com.ideaaedi.mybatis.data.security.enums.TypeEnum.STRING;


/**
 * 加解密信息持有器

* * 本类虽然涉及有方法解析,但是由于非常简单,所以就自己写了。如果你需要全面解析java代码,但是你又没有时间或精力去自己写,那么你可以考虑使用这两款工具:
*
    *
  • JavaParser:强大、全面,学习曲线较峭
  • *
  • QDox:基本够用,学习曲线较缓
  • * *
* * @author JustryDeng * @since 2021/2/10 22:48:44 */ public class EncryptInfoHolder { private EncryptInfoHolder() { } /** sql对应的MappedStatement对象的Id */ private String mappedStatementId; /** mappedStatementId对应的方法 */ private Method targetMethod; /** 参数与参数的注解map */ private Map paramAnnotationMap; /** 返回值类型 */ private Class returnClass; /** 是否需要加密 */ private boolean needEncrypt; /** 要加密的bean信息 */ private List encryptBeanInfoList; /** 要加密的参数信息 */ private ParamEncryptDetailInfo encryptParamInfo; /** 是否需要解密 */ private boolean needDecrypt; /** 要解密的bean信息 */ private BeanEncryptDetailInfo decryptBeanInfo; public String getMappedStatementId() { return mappedStatementId; } private void setMappedStatementId(String mappedStatementId) { this.mappedStatementId = mappedStatementId; } public Method getTargetMethod() { return targetMethod; } private void setTargetMethod(Method targetMethod) { this.targetMethod = targetMethod; } public Map getParamAnnotationMap() { return paramAnnotationMap; } private void setParamAnnotationMap(Map paramAnnotationMap) { this.paramAnnotationMap = paramAnnotationMap; } public Class getReturnClass() { return returnClass; } private void setReturnClass(Class returnClass) { this.returnClass = returnClass; } public boolean isNeedEncrypt() { return needEncrypt; } private void setNeedEncrypt(boolean needEncrypt) { this.needEncrypt = needEncrypt; } public List getEncryptBeanInfoList() { return encryptBeanInfoList; } private void setEncryptBeanInfoList(List encryptBeanInfoList) { this.encryptBeanInfoList = encryptBeanInfoList; } public ParamEncryptDetailInfo getEncryptParamInfo() { return encryptParamInfo; } private void setEncryptParamInfo(ParamEncryptDetailInfo encryptParamInfo) { this.encryptParamInfo = encryptParamInfo; } public boolean isNeedDecrypt() { return needDecrypt; } private void setNeedDecrypt(boolean needDecrypt) { this.needDecrypt = needDecrypt; } public BeanEncryptDetailInfo getDecryptBeanInfo() { return decryptBeanInfo; } private void setDecryptBeanInfo(BeanEncryptDetailInfo decryptBeanInfo) { this.decryptBeanInfo = decryptBeanInfo; } @Override public String toString() { return "EncryptInfoHolder{" + "mappedStatementId='" + mappedStatementId + '\'' + ", targetMethod=" + targetMethod + ", paramAnnotationMap=" + paramAnnotationMap + ", returnClass=" + returnClass + ", needEncrypt=" + needEncrypt + ", encryptBeanInfoList=" + encryptBeanInfoList + ", encryptParamInfo=" + encryptParamInfo + ", needDecrypt=" + needDecrypt + ", decryptBeanInfo=" + decryptBeanInfo + '}'; } /** * bean的具体加密/解密信息 */ public static class BeanEncryptDetailInfo { private BeanEncryptDetailInfo() { } /** 字段-加解密信息 map */ private Map fieldEncryptMap; /** 当前BeanEncryptDetailInfo对象所执行的bean的类型 */ private Class beanClass; /** * 创建BeanEncryptDetailInfo实例 * * @param beanClass * 待解析的class * @return BeanEncryptDetailInfo实例 */ @NonNull public static BeanEncryptDetailInfo build(Class beanClass) { BeanEncryptDetailInfo beanEncryptDetailInfo = new BeanEncryptDetailInfo(); beanEncryptDetailInfo.setBeanClass(beanClass); beanEncryptDetailInfo.setFieldEncryptMap(extractBeanFieldEncryptInfo(beanClass)); return beanEncryptDetailInfo; } /** * 解析beanClass中的@Encrypt注解 * * @param beanClass * 待解析的class * @return class中的 字段-加密信息map */ public static Map extractBeanFieldEncryptInfo(Class beanClass) { Map infoMap = new HashMap<>(8); ReflectionUtils.doWithFields(beanClass, (Field field) ->{ Encrypt annotation = field.getAnnotation(Encrypt.class); if (annotation != null) { // [强制约束] 当@Encrypt应用于ElementType.FIELD上时,对应的字段类型必须是String Class fieldType = field.getType(); if (TypeUtils.isAssignable(fieldType, String.class)) { infoMap.put(field, annotation); } else { throw new IllegalArgumentException( String.format("@Encrypt is not allowed to apply at type [%s], only for String,class. @see %s", fieldType, beanClass.getName()) ); } } }); // 根据Encrypt的order值对map进行排序 return new ArrayList<>(infoMap.entrySet()).stream() .sorted(Comparator.comparing(fieldEncryptEntry -> fieldEncryptEntry.getValue().order())) .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (u, v) -> { throw new IllegalStateException("Duplicate key " + u); }, LinkedHashMap::new ) ); } public Map getFieldEncryptMap() { return fieldEncryptMap; } private void setFieldEncryptMap(Map fieldEncryptMap) { this.fieldEncryptMap = fieldEncryptMap; } public Class getBeanClass() { return beanClass; } private void setBeanClass(Class beanClass) { this.beanClass = beanClass; } @Override public String toString() { return "BeanEncryptDetailInfo{" + "fieldEncryptMap=" + fieldEncryptMap + ", beanClass=" + beanClass + '}'; } } /** * ElementType.PARAMETER 参数的具体加密=信息 */ public static class ParamEncryptDetailInfo { private ParamEncryptDetailInfo() { } public static final ParamEncryptDetailInfo EMPTY = new ParamEncryptDetailInfo(); /** ({@link Param}注解指定的)参数名 - 加解密信息 map */ private Map paramEncryptMap; public Map getParamEncryptMap() { return paramEncryptMap; } private void setParamEncryptMap(Map paramEncryptMap) { this.paramEncryptMap = paramEncryptMap; } @Override public String toString() { return "ParamEncryptDetailInfo{" + "paramEncryptMap=" + paramEncryptMap + '}'; } } /** * EncryptInfoHolder工厂 */ public static class Factory { private static final Logger log = LoggerFactory.getLogger(Factory.class); /** 泛型左符号 */ private static final String GENERIC_LEFT_SIGN = "<"; /** 泛型右符号 */ private static final String GENERIC_RIGHT_SIGN = ">"; /** 获取方法签名的Method */ private static final Method GET_GENERIC_SIGNATURE; static { GET_GENERIC_SIGNATURE = ReflectionUtils.findMethod(Method.class, "getGenericSignature"); //noinspection ConstantConditions GET_GENERIC_SIGNATURE.setAccessible(true); } /** * 创建EncryptInfoHolder实例 */ public static EncryptInfoHolder create(String mappedStatementId, Method targetMethod, Parameter[] parameters, Annotation[][] paramAnnotations, Class returnClass) { EncryptInfoHolder instance = new EncryptInfoHolder(); instance.setMappedStatementId(mappedStatementId); instance.setTargetMethod(targetMethod); Map tmpMap = new HashMap<>(8); for (int i = 0; i < parameters.length; i++) { tmpMap.put(parameters[i], paramAnnotations[i]); } instance.setParamAnnotationMap(tmpMap); instance.setReturnClass(returnClass); Pair, ParamEncryptDetailInfo> encryptPair = analyzeEncryptDetailInfo(mappedStatementId, tmpMap); instance.setEncryptBeanInfoList(encryptPair.getLeft()); instance.setEncryptParamInfo(encryptPair.getRight()); boolean needEncrypt = (!CollectionUtils.isEmpty(instance.getEncryptBeanInfoList())) || (!CollectionUtils.isEmpty(instance.getEncryptParamInfo().getParamEncryptMap())); instance.setNeedEncrypt(needEncrypt); BeanEncryptDetailInfo decryptBeanInfo = analyzeDecryptDetailInfo(targetMethod, returnClass); instance.setDecryptBeanInfo(decryptBeanInfo); boolean needDecrypt = decryptBeanInfo != null && (!CollectionUtils.isEmpty(decryptBeanInfo.fieldEncryptMap)); instance.setNeedDecrypt(needDecrypt); log.debug("EncryptInfoHolder$Factory parse mappedStatementId [{}], create instance -> {}", mappedStatementId, instance); return instance; } /** * 分析加密信息详情 * * @param mappedStatementId * sql对应的mappedStatementId * @param paramAnnotationMap * 参数与注解map * @return 加密信息详情 *
    *
  • 左 - 对bean的具体加密信息
  • *
  • 右 - 对参数的具体加密信息
  • *
*/ private static Pair, ParamEncryptDetailInfo> analyzeEncryptDetailInfo(String mappedStatementId, Map paramAnnotationMap) { List encryptBeanInfoList = new ArrayList<>(8); ParamEncryptDetailInfo encryptParamInfo = new ParamEncryptDetailInfo(); Map paramEncryptMap = new HashMap<>(8); encryptParamInfo.setParamEncryptMap(paramEncryptMap); // 遍历参数进行解析 paramAnnotationMap.forEach((param, annotations) -> { Class type = param.getType(); TypeEnum typeEnum = TypeEnum.parseType(type); // [强制约束] 当@Encrypt应用于ElementType.PARAMETER前时,参数项类型必须是String,不能是其它的 if (typeEnum != STRING) { for (Annotation annotation : annotations) { if (annotation instanceof Encrypt) { throw new IllegalArgumentException(String.format("@Encrypt is not allowed to apply at type [%s]. @see %s", type, mappedStatementId)); } } } // 如果是基础类型或者是其包装类型就跳过 if (typeEnum == PRIMITIVE_OR_WRAPPER) { return; } // string if (typeEnum == STRING) { Encrypt encryptAnnotation = (Encrypt)Arrays.stream(annotations).filter(x -> x instanceof Encrypt).findFirst().orElse(null); Param paramAnnotation = (Param)Arrays.stream(annotations).filter(x -> x instanceof Param).findFirst().orElse(null); // [强制约束] 当@Encrypt应用于ElementType.PARAMETER前时,还需同时使用@Param指定名称 if (encryptAnnotation != null) { if (paramAnnotation == null) { throw new IllegalArgumentException(String.format("While use @Encrypt in case ElementType.PARAMETER, " + "must use @Param at the same time. @see %s", mappedStatementId)); } paramEncryptMap.put(paramAnnotation.value(), encryptAnnotation); } return; } // map、collection、array if (typeEnum == MAP || typeEnum == COLLECTION || typeEnum == ARRAY) { Pair> typeEnumClassPair = parseInnermostType(param, mappedStatementId); if (typeEnumClassPair.getLeft() == CUSTOM_BEAN) { BeanEncryptDetailInfo info = BeanEncryptDetailInfo.build(typeEnumClassPair.getRight()); if (!CollectionUtils.isEmpty(info.getFieldEncryptMap())) { encryptBeanInfoList.add(info); } } return; } if (typeEnum == CUSTOM_BEAN) { BeanEncryptDetailInfo info = BeanEncryptDetailInfo.build(type); if (!CollectionUtils.isEmpty(info.getFieldEncryptMap())) { encryptBeanInfoList.add(info); } } else { log.debug("Ignore analyze bean of type [{}]. @see {}", type.getName(), mappedStatementId); } }); return Pair.of(encryptBeanInfoList, encryptParamInfo); } /** * 根据返回值类型,判断是否需要解密 *

* 理论支持:如果方法的参数或者返回值存在显示指定的泛型时,为了避免因泛型擦除而导致在某些场景下的定位混乱,所以JVM会为那些有泛型的方法生成签名,如: *

    *
  • 1. Map method1(@Param("name") String name); *
    * null 这种不指定具体泛型的,是不会生成签名的(即:获取到的签名为null). *
  • *
  • * 2. Map method2(@Param("p") Map paramsMap); *
    * (Ljava/util/Map;)Ljava/util/Map; *
  • *
  • * 3. Map method3(@Param("id") Integer id); *
    * (Ljava/lang/Integer;)Ljava/util/Map; *
  • *
  • * 4, int method4(@Param("p") Map paramsMap); *
    * (Ljava/util/Map;)I *
  • *
  • * 5. List> method5(@Param("name") String name); *
    * (Ljava/lang/String;)Ljava/util/List;>; *
  • *
  • * 6. List method6(); *
    * ()Ljava/util/List; *
  • *
  • * 7. Employee[] method7(@Param("p") Map paramsMap); *
    * (Ljava/util/Map;)[Lcom/aspire/ssm/model/Employee; *
  • *
  • * 8. Employee[][] method8(@Param("p") Map paramsMap); *
    * (Ljava/util/Map;)[[Lcom/aspire/ssm/model/Employee; *
  • *
  • * 9。 Map method9(@Param("p1") Map paramsMap1, @Param("p2") Map paramsMap2); *
    * (Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map; *
  • *
*

*

* 提示:简单的,可以使用以下代码输出类(如AbcMapper)中所有方法的签名 * * Method getGenericSignature = ReflectionUtils.findMethod(Method.class, "getGenericSignature"); * getGenericSignature.setAccessible(true); * Method[] declaredMethods = AbcMapper.class.getDeclaredMethods(); * for (Method declaredMethod : declaredMethods) { * System.err.println(i + "\t" + ReflectionUtils.invokeMethod(getGenericSignature, declaredMethod)); * } * *

*/ @SuppressWarnings("AlibabaUndefineMagicConstant") @Nullable private static BeanEncryptDetailInfo analyzeDecryptDetailInfo(Method targetMethod, Class returnClass){ TypeEnum typeEnum = TypeEnum.parseType(returnClass); if (typeEnum == CUSTOM_BEAN) { return BeanEncryptDetailInfo.build(returnClass); } if (typeEnum == MAP || typeEnum == COLLECTION) { Object getGenericSignature = ReflectionUtils.invokeMethod(GET_GENERIC_SIGNATURE, targetMethod); // 返回值没有明确的指定泛型,如上面的示例1 if (getGenericSignature == null) { return null; } // 返回值没有明确的指定泛型,但由于参数存在泛型,导致getGenericSignature不为null的情况,如上面的示例2 String signature = getGenericSignature.toString(); String returnObjSignature = signature.substring(signature.lastIndexOf(")") + 1); int startIndex = returnObjSignature.indexOf(GENERIC_LEFT_SIGN); int endIndex = returnObjSignature.lastIndexOf(GENERIC_RIGHT_SIGN); if (startIndex == -1 || endIndex == -1) { return null; } // 此时,获取到的泛型,在上述示例3中体现为 Ljava/lang/String;Ljava/lang/Object; // 此时,获取到的泛型,在上述示例5中体现为 Ljava/util/Map; String genericInfo = returnObjSignature.substring(startIndex + 1, endIndex); // [强制约束] 不支持泛型嵌套泛型这样的复杂写法(如上述示例5),快速失败,使项目启动不起来 if (genericInfo.contains(GENERIC_LEFT_SIGN) || genericInfo.contains(GENERIC_RIGHT_SIGN)) { throw new UnsupportedOperationException(String.format("Cannot support return-type Generics nested Generics. @see %s", targetMethod)); } // 逻辑到这里,已经保证了返回值的有泛型,且泛型一定是(类似于上述示例3那样的)非嵌套泛型 // Map的泛型是k-v的形式,要获取第二个; Collection直接获取第一个 int genericInfoIndex = typeEnum == MAP ? 1 : 0; String onlyGenericInfo = genericInfo.split(";")[genericInfoIndex]; // [强制约束] 不允许泛型是数组,快速失败,使项目启动不起来 if (onlyGenericInfo.startsWith("[")) { throw new UnsupportedOperationException(String.format("Cannot support return-type Generics exist Array. @see %s", targetMethod)); } // (因为基本类型不能作为泛型且上面考虑了数组的情况,所以)到这里onlyGenericInfo就一定是L打头的了, L表示对象类型, 即:此时,onlyGenericInfo的值形如 Lcom/aspire/ssm/model/Employee String elementClassName = onlyGenericInfo.substring(1).replace("/", "."); Class elementClass; try { elementClass = Class.forName(elementClassName); }catch (ClassNotFoundException e) { throw new IllegalStateException(String.format("Cannot find class [%s], while signature is [%s]. targetMethod is [%s].", elementClassName, signature, targetMethod), e); } typeEnum = TypeEnum.parseType(elementClass); if (typeEnum == CUSTOM_BEAN) { return BeanEncryptDetailInfo.build(elementClass); } else { return null; } } if (typeEnum == ARRAY) { String clazzName = returnClass.getName(); // [强制约束] 返回值类型不能是多维数组 if (clazzName.startsWith("[[")) { throw new UnsupportedOperationException(String.format("Cannot support return-type is multidimensional Array. @see %s", targetMethod)); } if (!clazzName.startsWith("[L")) { // 如果不是对象数组 (如:是基本类型数组)的话,直接返回null return null; } // 逻辑到这里,即保证了返回值类型一定是一维数组,且是一维非基本类型数组 // 去掉开头的[L和结尾;号 clazzName = clazzName.substring(2, clazzName.length() - 1); Class beanClass; try { beanClass = Class.forName(clazzName); typeEnum = TypeEnum.parseType(beanClass); return typeEnum == CUSTOM_BEAN ? BeanEncryptDetailInfo.build(beanClass) : null; } catch (ClassNotFoundException e) { throw new IllegalStateException(String.format("Cannot find class [%s], targetMethod is [%s].", clazzName, targetMethod), e); } } return null; } /** * 解析parameter的里层数据类型 *

* 示例: *

    *
  • Collection => 最里层的bean类型为E
  • *
  • Map => 最里层的bean类型为v
  • *
  • 一维数组[A] => 最里层的bean类型为A
  • *
  • ......
  • *
*

* * @param parameter * 待解析的参数信息 * @param mappedStatementId * mappedStatementId可定位parameter所在方法的 * @return
    *
  • 左 - 数据类型
  • *
  • 右 - 具体的class
  • *
*

注:此方法返回的TpeEnum,不可能为COLLECTION或MAP或ARRAY

*/ private static Pair> parseInnermostType(Parameter parameter, String mappedStatementId) { Class clazz = parameter.getType(); TypeEnum typeEnum = TypeEnum.parseType(clazz); switch (typeEnum) { case PRIMITIVE_OR_WRAPPER: case STRING: case CUSTOM_BEAN: case SYSTEM_BEAN: return Pair.of(typeEnum, clazz); case COLLECTION: case MAP: Type parameterizedType = parameter.getParameterizedType(); if (parameterizedType instanceof ParameterizedTypeImpl) { // 集合只有一个泛型,所以解析index=0的泛型;MAP解析value的类型,所以解析index=1的泛型 int targetTypeIndex = typeEnum == COLLECTION ? 0 : 1; String genericClassName = ((ParameterizedTypeImpl) parameterizedType).getActualTypeArguments()[targetTypeIndex].getTypeName(); Class genericClass; try { genericClass = Class.forName(genericClassName); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(String.format("Cannot load class [%s]. At param [%s]. @see %s", genericClassName, parameter.getName(), mappedStatementId)); } TypeEnum genericTypeEnum = TypeEnum.parseType(genericClass); // fail fast不支持复杂的泛型(如:泛型嵌套泛型等) if (genericTypeEnum == MAP || genericTypeEnum == COLLECTION || genericTypeEnum == ARRAY) { throw new UnsupportedOperationException(String.format("Cannot support parse complex Generics nested Generics. " + "At param[%s]. @see %s", parameter.getName(), mappedStatementId)); } return Pair.of(genericTypeEnum, genericClass); } else { // fail fast不支持解析非ParameterizedTypeImpl类型的泛型 throw new UnsupportedOperationException(String.format("Cannot support parse non-ParameterizedTypeImpl Generics. " + "Maybe you should point specific generics at param[%s]. @see %s", parameter.getName(), mappedStatementId)); } case ARRAY: Type arrayComponentType = TypeUtils.getArrayComponentType(clazz); if (arrayComponentType instanceof Class) { Class arrayComponentClazz = (Class)arrayComponentType; TypeEnum arrayComponentTypeEnum = TypeEnum.parseType(arrayComponentClazz); // fail fast不支持复杂的数组嵌套 if (arrayComponentTypeEnum == MAP || arrayComponentTypeEnum == COLLECTION || arrayComponentTypeEnum == ARRAY) { throw new UnsupportedOperationException(String.format("Cannot support parse complex nesting Array. At param[%s]. @see %s", parameter.getName(), mappedStatementId)); } return Pair.of(arrayComponentTypeEnum, arrayComponentClazz); } else { // fail fast不支持解析非Class的实现 throw new UnsupportedOperationException(String.format("Cannot support parse non-Class Type. " + "At param[%s]. @see %s", parameter.getName(), mappedStatementId)); } default: throw new UnsupportedOperationException(String.format("Cannot support for typeEnum [%s] At param [%s]. @see %s", typeEnum, parameter.getName(), mappedStatementId)); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy