14 changed files with 250 additions and 3 deletions
@ -0,0 +1,44 @@ |
|||||
|
package com.epmet.commons.tools.annotation; |
||||
|
|
||||
|
import com.epmet.commons.tools.enums.RequirePermissionEnum; |
||||
|
import javassist.runtime.Inner; |
||||
|
|
||||
|
import java.lang.annotation.*; |
||||
|
import java.util.function.Function; |
||||
|
|
||||
|
/** |
||||
|
* 标记一个接口,它的返回值中的某些字段需要打掩码 |
||||
|
*/ |
||||
|
@Target(ElementType.METHOD) |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
@Documented |
||||
|
public @interface MaskResponse { |
||||
|
|
||||
|
/** |
||||
|
* 掩码类型 |
||||
|
*/ |
||||
|
String MASK_TYPE_ID_CARD = "ID_CARD"; |
||||
|
String MASK_TYPE_MOBILE = "MOBILE"; |
||||
|
|
||||
|
///**
|
||||
|
// * 默认的一些字段,如果没有手动指定,就会使用默认的。如果手动指定了,就不再使用默认的
|
||||
|
// */
|
||||
|
//String[] defaultFieldnames = {"idCard","mobile","phone"};
|
||||
|
//
|
||||
|
///**
|
||||
|
// * 默认字段对应的掩码类型
|
||||
|
// */
|
||||
|
//String[] defaultFieldsMaskType = { MASK_TYPE_ID_CARD, MASK_TYPE_MOBILE, MASK_TYPE_MOBILE };
|
||||
|
|
||||
|
/** |
||||
|
* 要打码的字段列表。会递归的着这些字段 |
||||
|
* @return |
||||
|
*/ |
||||
|
String[] fieldNames() default {"idCard","mobile","phone"}; |
||||
|
|
||||
|
/** |
||||
|
* 要打码的类型 |
||||
|
* @return |
||||
|
*/ |
||||
|
String[] fieldsMaskType() default { MASK_TYPE_ID_CARD, MASK_TYPE_MOBILE, MASK_TYPE_MOBILE }; |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
package com.epmet.commons.tools.aspect; |
||||
|
|
||||
|
import com.epmet.commons.tools.annotation.MaskResponse; |
||||
|
import com.epmet.commons.tools.exception.EpmetErrorCode; |
||||
|
import com.epmet.commons.tools.exception.EpmetException; |
||||
|
import com.epmet.commons.tools.processor.MaskProcessor; |
||||
|
import com.epmet.commons.tools.utils.Result; |
||||
|
import org.aspectj.lang.JoinPoint; |
||||
|
import org.aspectj.lang.annotation.AfterReturning; |
||||
|
import org.aspectj.lang.annotation.Aspect; |
||||
|
import org.aspectj.lang.reflect.MethodSignature; |
||||
|
import org.springframework.core.annotation.Order; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Aspect |
||||
|
@Component |
||||
|
@Order(0) |
||||
|
public class MaskResponseAspect { |
||||
|
|
||||
|
@AfterReturning(pointcut = "@annotation(com.epmet.commons.tools.annotation.MaskResponse)", returning = "result") |
||||
|
public Object proceed(JoinPoint point, Result result) throws Throwable { |
||||
|
MethodSignature signature = (MethodSignature) point.getSignature(); |
||||
|
MaskResponse maskResponseAnno = signature.getMethod().getAnnotation(MaskResponse.class); |
||||
|
|
||||
|
String[] fieldNames = maskResponseAnno.fieldNames(); |
||||
|
String[] fieldsMaskType = maskResponseAnno.fieldsMaskType(); |
||||
|
|
||||
|
if (fieldNames.length != fieldsMaskType.length) { |
||||
|
String msg = "掩码配置错误"; |
||||
|
throw new EpmetException(EpmetErrorCode.SERVER_ERROR.getCode(), msg, msg); |
||||
|
} |
||||
|
|
||||
|
new MaskProcessor(fieldNames, fieldsMaskType).mask(result); |
||||
|
return null; |
||||
|
} |
||||
|
} |
@ -0,0 +1,142 @@ |
|||||
|
package com.epmet.commons.tools.processor; |
||||
|
|
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import com.epmet.commons.tools.annotation.MaskResponse; |
||||
|
import com.epmet.commons.tools.exception.ExceptionUtils; |
||||
|
import com.epmet.commons.tools.page.PageData; |
||||
|
import com.epmet.commons.tools.utils.Result; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
|
||||
|
import java.lang.reflect.Field; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class MaskProcessor { |
||||
|
|
||||
|
|
||||
|
public static final String EPMET_PACKAGE_PREFIX = "com.epmet"; |
||||
|
|
||||
|
private List<String> fieldNames; |
||||
|
private List<String> fieldsMaskType; |
||||
|
|
||||
|
public MaskProcessor(String[] fields, String[] fieldsMaskType) { |
||||
|
if (fields != null && fields.length > 0) { |
||||
|
this.fieldNames = Arrays.asList(fields); |
||||
|
this.fieldsMaskType = Arrays.asList(fieldsMaskType); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 为dto中的属性打掩码 |
||||
|
* @param object |
||||
|
*/ |
||||
|
public void mask(Object object) { |
||||
|
if (object == null) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (object instanceof Result) { |
||||
|
mask(((Result<?>) object).getData()); |
||||
|
return; |
||||
|
} else if (object instanceof PageData) { |
||||
|
mask(((PageData<?>) object).getList()); |
||||
|
return; |
||||
|
} else if (object instanceof List) { |
||||
|
((List)object).forEach(e -> mask(e)); |
||||
|
return; |
||||
|
} else if (object instanceof Map) { |
||||
|
((Map) object).values().forEach(v -> mask(v)); |
||||
|
return; |
||||
|
} else if (object.getClass().getName().startsWith(EPMET_PACKAGE_PREFIX)) { |
||||
|
// 自定义bean,走反射
|
||||
|
maskEpmetBean(object); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 反射 |
||||
|
* @param object |
||||
|
*/ |
||||
|
private void maskEpmetBean(Object object) { |
||||
|
Field[] declaredFields = object.getClass().getDeclaredFields(); |
||||
|
for (Field currentField : declaredFields) { |
||||
|
currentField.setAccessible(true); |
||||
|
try { |
||||
|
String fieldName = currentField.getName(); |
||||
|
Object value = currentField.get(object); |
||||
|
// 是epmet的类,继续下钻
|
||||
|
if (currentField.getClass().getName().startsWith(EPMET_PACKAGE_PREFIX)) { |
||||
|
maskEpmetBean(value); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// 是字符串
|
||||
|
String fieldValue; |
||||
|
if (value instanceof String && StringUtils.isNotBlank(fieldValue = (String) value)) { |
||||
|
int fieldIndexInAnnoAttrs = fieldNames.indexOf(fieldName); |
||||
|
if (fieldIndexInAnnoAttrs != -1) { |
||||
|
String product = maskString(fieldValue, fieldsMaskType.get(fieldIndexInAnnoAttrs)); |
||||
|
currentField.set(object, product); |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// 非字符串,非epmet类的其他类型
|
||||
|
mask(value); |
||||
|
} catch (IllegalAccessException e) { |
||||
|
log.error("【mask一些字段报错】{}", ExceptionUtils.getErrorStackTrace(e)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 把字符串变更为掩码 |
||||
|
* @param originString |
||||
|
* @return |
||||
|
*/ |
||||
|
public String maskString(String originString, String maskType) { |
||||
|
if (MaskResponse.MASK_TYPE_ID_CARD.equals(maskType)) { |
||||
|
return maskIdCard(originString); |
||||
|
} else if (MaskResponse.MASK_TYPE_MOBILE.equals(maskType)) { |
||||
|
return maskMobile(originString); |
||||
|
} else { |
||||
|
return originString; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将明文字符串打码变为掩码。保留前6,后面打码 |
||||
|
* @param originString |
||||
|
* @return |
||||
|
*/ |
||||
|
private String maskIdCard(String originString) { |
||||
|
// 仅将6位之后的全都打码
|
||||
|
int length = originString.length(); |
||||
|
if (length <= 6) { |
||||
|
return originString; |
||||
|
} |
||||
|
|
||||
|
return originString.replace(originString.substring(6), StrUtil.repeatByLength("*", length - 6)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将明文字符串打码变为掩码。保留前3后4,中间打码 |
||||
|
* 187****3461 |
||||
|
* @param originString |
||||
|
* @return |
||||
|
*/ |
||||
|
private String maskMobile(String originString) { |
||||
|
int length = originString.length(); |
||||
|
if (length <= 7) { |
||||
|
return originString; |
||||
|
} |
||||
|
|
||||
|
String maskStr = StrUtil.repeatByLength("*", length - 7); |
||||
|
|
||||
|
return originString.replaceAll("^(1\\d{2})\\d*(\\d{4})$", new StringBuilder("$1").append(maskStr).append("$2").toString()); |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue