package com.pay.trade.util;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pay.trade.annoation.Desensitized;
import com.pay.trade.constant.SensitiveMap;
import com.pay.trade.enums.SensitiveType;

/**
*
* 敏感信息屏蔽工具
*/
public final class SensitiveInfoUtils {

private static Logger logger = LoggerFactory.getLogger(SensitiveInfoUtils.class);

private static final String NULLSTR = "null";

/**
* [中文姓名] 只显示第一个汉字,其他隐藏为星号<例子:李**>
*
* @param name
* @return
*/
public static String chineseName(String fullName) {
if (StringUtils.isBlank(fullName)) {
return fullName;
}
String name = StringUtils.left(fullName, 1);
return StringUtils.rightPad(name, 2, "*");
}

/**
* [证件号码类](身份证,军官证,护照等身份证明证件类) 后8位用******(6个*)代替。不足8位,直接******(6个*)代替
*
* @param id
* @return
*/
public static String idCardNum(String id) {
if (StringUtils.isBlank(id)) {
return id;
}
if (id.length() > 8) {
return id.replaceAll(StringUtils.right(id, 8), "******");
}
return "******";
}

/**
* [固定电话] 后四位,其他隐藏<例子:****1234>
*
* @param num
* @return
*/
public static String fixedPhone(String phone) {
if (StringUtils.isBlank(phone)) {
return phone;
}
if (phone.length() > 4) {
return StringUtils.leftPad(StringUtils.right(phone, 4), StringUtils.length(phone), "*");
}
return "******";
}

/**
* [手机号码类](联系人电话,个人手机) 前3位显示,后4位显示,中间部分******(6个*)代替 不足7位直接******(6个*)代替
*
* @param num
* @return
*/
public static String mobilePhone(String phone) {
if (StringUtils.isBlank(phone)) {
return phone;
}
if (phone.length() > 7) {
return StringUtils.left(phone, 3) + "******" + (StringUtils.right(phone, 4));
}
return "******";
}

/**
* 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
*
* @param address
* @param sensitiveSize
* 敏感信息长度
* @return
*/
public static String address(String address, int sensitiveSize) {
if (StringUtils.isBlank(address)) {
return "";
}
int length = StringUtils.length(address);
return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
}

/**
* [地址类] 前2位显示 ,后2位显示,中间部分******(6个*)代替 不足4位全部******(6个*)代替
*
* @param address
* @return
*/
public static String address(String address) {
if (StringUtils.isBlank(address)) {
return address;
}
if (address.length() > 4) {
return StringUtils.left(address, 2) + "******" + StringUtils.right(address, 2);
}
return "******";
}

/**
* [邮箱类]******.com / (.)号之前全部******(6个*)代替,(.)号之后显示出来
*
* @param email
* @return
*/
public static String email(String email) {
if (StringUtils.isBlank(email)) {
return email;
}
int index = email.lastIndexOf(".");
if (index > 1) {
return "******" + StringUtils.right(email, email.length() - index);
}
return "******";
}

/**
* [卡号 ] 前2位显示,后4位显示,中间部分******(6个*)代替
*
* @param cardNum
* @return
*/
public static String bankCard(String cardNum) {
if (StringUtils.isBlank(cardNum)) {
return cardNum;
}
if (cardNum.length() > 6) {
return StringUtils.left(cardNum, 2) + "******" + StringUtils.right(cardNum, 4);
}
return "******";
}

/**
* [银行名] 显示前4位
*
* @param bankName
* @return
*/
public static String bankName(String bankName) {
if (StringUtils.isBlank(bankName)) {
return bankName;
}
if (bankName.length() > 4) {
return StringUtils.rightPad(StringUtils.left(bankName, 4), StringUtils.length(bankName), "*");
}
return StringUtils.rightPad(StringUtils.left(bankName, 1), StringUtils.length(bankName), "*");
}

/**
* [统一社会信用代码类(注册码,营业执照,牌照类的码) ] 前4位显示,后4位显示,中间部分******(6个*)代替
*
* @param code
* @return
*/
public static String cnapsCode(String code) {
if (StringUtils.isBlank(code)) {
return code;
}
if (code.length() > 8) {
return StringUtils.left(code, 4) + "******" + StringUtils.right(code, 4);
}
return "******";
}

/**
* [银行卡有效期] 前1位,后1位,其他隐藏<例子:“0**6”>
*
* @param num
* @return
*/
public static String cardValidDate(String date) {
if (StringUtils.isBlank(date)) {
return date;
}
return StringUtils.left(date, 1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(date, 1), StringUtils.length(date), "*"), "*"));
}

/**
* 全部隐藏
*
* @param num
* @return
*/
public static String all(String data, int sensitiveSize) {
return StringUtils.repeat("*", sensitiveSize);
}

/**
* 根据信息类型屏蔽<br>
*
* @param type
* 信息类型
* @param key
* 信息key 如bankaccountno
* @param value
* 信息value 如 62121212312312
* @return
*/
public static String convertMsg(SensitiveType type, String value) {
if (StringUtils.isBlank(value)) {
return "";
}
// 返回null 时保持原样返回方便查问题
if (NULLSTR.equalsIgnoreCase(value)) {
return value;
}
switch (type) {
case CHINESE_NAME: {
value = SensitiveInfoUtils.chineseName(value);
break;
}
case ID_CARD: {
value = SensitiveInfoUtils.idCardNum(value);
break;
}
case FIXED_PHONE: {
value = SensitiveInfoUtils.fixedPhone(value);
break;
}
case MOBILE_PHONE: {
value = SensitiveInfoUtils.mobilePhone(value);
break;
}
case ADDRESS: {
value = SensitiveInfoUtils.address(value);
break;
}
case EMAIL: {
value = SensitiveInfoUtils.email(value);
break;
}
case BANK_CARD: {
value = SensitiveInfoUtils.bankCard(value);
break;
}
case BANK_NAME: {
value = SensitiveInfoUtils.bankName(value);
break;
}
case CNAPS_CODE: {
value = SensitiveInfoUtils.cnapsCode(value);
break;
}
case BANK_CARD_DATE: {
value = SensitiveInfoUtils.cardValidDate(value);
break;
}
case PASSWORD: {
value = SensitiveInfoUtils.all(value, 6);
break;
}
case ALL: {
value = SensitiveInfoUtils.all(value, 3);
break;
}
case NULL: {
value = null;
break;
}
default:
break;
}
return value;
}

/**
* 对bean对象转换成脱敏后的对象
*
* @param javaBean
* @throws Exception
*/
public static void processObject(Object javaBean) throws Exception {
if (javaBean == null) {
return;
}
if (javaBean.getClass().isInterface()) {
return;
}
/* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
Set<Integer> referenceCounter = new HashSet<Integer>();
/* 对克隆实体进行脱敏操作 */
replace(getAllFields(javaBean), javaBean, referenceCounter);
/* 清空计数器 */
referenceCounter.clear();
referenceCounter = null;
}

/**
* 对bean字符串输出
*
* @param javaBean
* @return
* @throws Exception
*/
public static String getObjectStr(Object javaBean) {
if (javaBean == null) {
logger.debug("javaBean is null");
return null;
}
if (javaBean.getClass().isInterface()) {
logger.debug("javaBean is interface");
return null;
}
Set<Integer> referenceCounter = new HashSet<Integer>();
try {
StringBuilder sb = new StringBuilder();
sb.append(javaBean.getClass().getSimpleName()).append(" [");
getFiledStr(getAllFields(javaBean), javaBean, referenceCounter, sb);
sb.append("]");
return sb.toString().replaceAll(", ]", "]");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 对需要脱敏的字段进行转化
*
* @param fields
* @param javaBean
* @param referenceCounter
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private static void replace(Field[] fields, Object javaBean, Set<Integer> referenceCounter) throws IllegalArgumentException, IllegalAccessException {
if (null != fields && fields.length > 0) {
for (Field field : fields) {
field.setAccessible(true);
if (null != field && null != javaBean) {
Object value = field.get(javaBean);
if (null != value) {
Class<?> type = value.getClass();
// 处理子属性,包括集合中的
if (type.isArray()) {// 对数组类型的字段进行递归过滤
int len = Array.getLength(value);
for (int i = 0; i < len; i++) {
Object arrayObject = Array.get(value, i);
if (arrayObject == null) {
continue;
}
if (isNotGeneralType(arrayObject.getClass(), arrayObject, referenceCounter)) {
replace(getAllFields(arrayObject), arrayObject, referenceCounter);
}
}
} else if (value instanceof Collection<?>) {// 对集合类型的字段进行递归过滤
Collection<?> c = (Collection<?>) value;
Iterator<?> it = c.iterator();
while (it.hasNext()) {// TODO: 待优化
Object collectionObj = it.next();
if (collectionObj == null) {
continue;
}
if (isNotGeneralType(collectionObj.getClass(), collectionObj, referenceCounter)) {
replace(getAllFields(collectionObj), collectionObj, referenceCounter);
}
}
} else if (value instanceof Map<?, ?>) {// 对Map类型的字段进行递归过滤
Map<?, ?> m = (Map<?, ?>) value;
Set<?> set = m.entrySet();
if (set == null) {
continue;
}
for (Object o : set) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
Object mapVal = entry.getValue();
if (mapVal == null) {
continue;
}
if (isNotGeneralType(mapVal.getClass(), mapVal, referenceCounter)) {
replace(getAllFields(mapVal), mapVal, referenceCounter);
}
}
} else if (value instanceof Enum<?>) {
continue;
}
/* 除基础类型、jdk类型的字段之外,对其他类型的字段进行递归过滤 */
else {
if (!type.isPrimitive() && type.getPackage() != null && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
&& !StringUtils.startsWith(type.getPackage().getName(), "java.")
&& !StringUtils.startsWith(field.getType().getName(), "javax.") && !StringUtils.startsWith(field.getName(), "java.")
&& referenceCounter.add(value.hashCode())) {
replace(getAllFields(value), value, referenceCounter);
}
}
}
// 脱敏操作
setNewValueForField(javaBean, field, value);
}
}
}
}

/**
* 脱敏操作(按照规则转化需要脱敏的字段并设置新值) 目前只支持String类型的字段,如需要其他类型如BigDecimal、Date等类型,可以添加
*
* @param javaBean
* @param field
* @param value
* @throws IllegalAccessException
*/
private static void setNewValueForField(Object javaBean, Field field, Object value) throws IllegalAccessException {
// 处理自身的属性
Desensitized annotation = field.getAnnotation(Desensitized.class);
if (field.getType().equals(String.class) && null != annotation && executeIsEffictiveMethod(javaBean, annotation)) {
String valueStr = (String) value;
if (StringUtils.isNotBlank(valueStr)) {
field.set(javaBean, SensitiveInfoUtils.convertMsg(annotation.type(), valueStr));
}
}
}

/**
* 执行某个对象中指定的方法
*
* @param javaBean
* 对象
* @param desensitized
* @return
*/
private static boolean executeIsEffictiveMethod(Object javaBean, Desensitized desensitized) {
boolean isAnnotationEffictive = true;// 注解默认生效
if (desensitized != null) {
String isEffictiveMethod = desensitized.isEffictiveMethod();
if (StringUtils.isNotBlank(isEffictiveMethod)) {
try {
Method method = javaBean.getClass().getMethod(isEffictiveMethod);
method.setAccessible(true);
isAnnotationEffictive = (Boolean) method.invoke(javaBean);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
return isAnnotationEffictive;
}

/**
* 排除基础类型、jdk类型、枚举类型的字段
*
* @param clazz
* @param value
* @param referenceCounter
* @return
*/
private static boolean isNotGeneralType(Class<?> clazz, Object value, Set<Integer> referenceCounter) {
return !clazz.isPrimitive() && clazz.getPackage() != null && !clazz.isEnum() && !StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
&& !StringUtils.startsWith(clazz.getPackage().getName(), "java.") && !StringUtils.startsWith(clazz.getName(), "javax.")
&& !StringUtils.startsWith(clazz.getName(), "java.") && referenceCounter.add(value.hashCode());
}

/**
* 获取包括父类所有的属性
*
* @param objSource
* @return
*/
private static Field[] getAllFields(Object objSource) {
/* 获得当前类的所有属性(private、protected、public) */
List<Field> fieldList = new ArrayList<Field>();
Class<?> tempClass = objSource.getClass();
while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {// 当父类为null的时候说明到达了最上层的父类(Object类).
fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass(); // 得到父类,然后赋给自己
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fields;
}

/**
* 获取包括父类所有的属性
*
* @param objSource
* @param sb
* @return
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
private static String getFiledStr(Field[] fields, Object objSource, Set<Integer> referenceCounter, StringBuilder sb)
throws IllegalArgumentException, IllegalAccessException {
/* 获得当前类的所有属性(private、protected、public) */
if (null != fields && fields.length > 0) {
for (Field field : fields) {
field.setAccessible(true);
if (null != field && null != objSource) {
Object value = field.get(objSource);
if (null != value) {
Class<?> type = value.getClass();
// 处理子属性,包括集合中的
if (type.isArray()) {// 对数组类型的字段进行递归过滤
Desensitized annotation = field.getAnnotation(Desensitized.class);
if (annotation != null) {
sb.append(field.getName()).append(" = [ *** ]");
} else {
int len = Array.getLength(value);
sb.append(field.getName()).append(" = [");
for (int i = 0; i < len; i++) {
Object arrayObject = Array.get(value, i);
if (arrayObject == null) {
continue;
}
sb.append(arrayObject.getClass().getSimpleName()).append(" [");
if (isNotGeneralType(arrayObject.getClass(), arrayObject, referenceCounter)) {
getFiledStr(getAllFields(arrayObject), arrayObject, referenceCounter, sb);
} else {
getFieldStr(objSource, field, value, sb);
}
sb.append("], ");
}
sb.append("], ");
}
} else if (value instanceof Collection<?>) {// 对集合类型的字段进行递归过滤
Desensitized annotation = field.getAnnotation(Desensitized.class);
if (annotation != null) {
sb.append(field.getName()).append(" = [ *** ]");
} else {
Collection<?> c = (Collection<?>) value;
Iterator<?> it = c.iterator();
sb.append(field.getName()).append(" = [");
while (it.hasNext()) {// TODO: 待优化
Object collectionObj = it.next();
if (collectionObj == null) {
continue;
}
sb.append(collectionObj.getClass().getSimpleName()).append(" [");
if (isNotGeneralType(collectionObj.getClass(), collectionObj, referenceCounter)) {
getFiledStr(getAllFields(collectionObj), collectionObj, referenceCounter, sb);
} else {
getFieldStr(objSource, field, value, sb);
}
sb.append("], ");
}
sb.append("], ");
}
} else if (value instanceof Map<?, ?>) {// 对Map类型的字段进行递归过滤
Map<?, ?> m = (Map<?, ?>) value;
Set<?> set = m.entrySet();
if (set == null) {
continue;
}
Desensitized annotation = field.getAnnotation(Desensitized.class);
if (annotation != null) {
sb.append(field.getName()).append(" = [ *** ]");
} else {
sb.append(field.getName()).append(" = [");
for (Object o : set) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
Object mapVal = entry.getValue();
if (mapVal == null) {
continue;
}
sb.append(entry.getKey()).append(" = ").append(mapVal.getClass().getSimpleName()).append(" [");
if (isNotGeneralType(mapVal.getClass(), mapVal, referenceCounter)) {
getFiledStr(getAllFields(mapVal), mapVal, referenceCounter, sb);
} else {
getFieldStr(objSource, field, value, sb);
}
sb.append("], ");
}
sb.append("], ");
}
} else if (value instanceof Enum<?>) {
continue;
} else if (!type.isPrimitive() && type.getPackage() != null && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
&& !StringUtils.startsWith(type.getPackage().getName(), "java.") && !StringUtils.startsWith(field.getType().getName(), "javax.")
&& !StringUtils.startsWith(field.getName(), "java.") && referenceCounter.add(value.hashCode())) {
Desensitized annotation = field.getAnnotation(Desensitized.class);
if (annotation != null) {
sb.append(field.getName()).append("=").append(type.getSimpleName()).append(" [ *** ]");
} else {
sb.append(field.getName()).append("=").append(type.getSimpleName()).append(" [");
getFiledStr(getAllFields(value), value, referenceCounter, sb);
sb.append("], ");
}
} else {
// 脱敏操作
getFieldStr(objSource, field, value, sb);
}
}
}
}
}
return sb.toString();
}

private static void getFieldStr(Object objSource, Field field, Object value, StringBuilder sb) {
// 处理自身的属性
Desensitized annotation = field.getAnnotation(Desensitized.class);
if (field.getType().equals(String.class) && null != annotation && executeIsEffictiveMethod(objSource, annotation)) {
String valueStr = (String) value;
if (StringUtils.isNotBlank(valueStr)) {
sb.append(field.getName()).append(" = ").append(SensitiveInfoUtils.convertMsg(annotation.type(), valueStr)).append(", ");
} else {
sb.append(field.getName()).append(" = null, ");
}
} else {
if (SensitiveMap.getSensitiveMap().containsKey(field.getName())) {
sb.append(field.getName()).append(" = ")
.append(SensitiveInfoUtils.convertMsg(SensitiveMap.getSensitiveMap().get(field.getName()), value == null ? null : value.toString()))
.append(", ");
} else {
sb.append(field.getName()).append(" = ").append(value).append(", ");
}
}
}
}