From a5acb554cdd8c3ab5c20b6a35632df6897e317fd Mon Sep 17 00:00:00 2001 From: applesline Date: Thu, 20 Jan 2022 11:54:59 +0000 Subject: [PATCH] init desensitize --- pom.xml | 91 ++++++++++++ .../desensitize/annotation/Desensitize.java | 17 +++ .../annotation/EnableDesensitize.java | 27 ++++ .../desensitize/annotation/FieldMapping.java | 15 ++ .../desensitize/aop/DesensitizeAdvice.java | 69 +++++++++ .../config/AutoDesensitizeConfiguration.java | 32 +++++ .../config/AutoDesensitizeRegistrar.java | 68 +++++++++ .../config/DesensitizeBeanPostProcessor.java | 38 +++++ .../constants/DesensitizeType.java | 22 +++ .../executor/DesensitizeExecutor.java | 23 +++ .../executor/DesensitizeExecutorAdapter.java | 57 ++++++++ .../executor/JsonPathDesensitizeExecutor.java | 73 ++++++++++ .../handle/DesensitizeAdapter.java | 131 ++++++++++++++++++ .../handle/DesensitizeHandler.java | 18 +++ .../handle/DesensitizeHandlerSelector.java | 30 ++++ .../handle/impl/AddressHandler.java | 28 ++++ .../handle/impl/BankCardHandler.java | 46 ++++++ .../handle/impl/BirthdayHandler.java | 22 +++ .../handle/impl/CarLicenseHandler.java | 32 +++++ src/main/resources/application.properties | 1 + 20 files changed, 840 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/applesline/desensitize/annotation/Desensitize.java create mode 100644 src/main/java/org/applesline/desensitize/annotation/EnableDesensitize.java create mode 100644 src/main/java/org/applesline/desensitize/annotation/FieldMapping.java create mode 100644 src/main/java/org/applesline/desensitize/aop/DesensitizeAdvice.java create mode 100644 src/main/java/org/applesline/desensitize/config/AutoDesensitizeConfiguration.java create mode 100644 src/main/java/org/applesline/desensitize/config/AutoDesensitizeRegistrar.java create mode 100644 src/main/java/org/applesline/desensitize/config/DesensitizeBeanPostProcessor.java create mode 100644 src/main/java/org/applesline/desensitize/constants/DesensitizeType.java create mode 100644 src/main/java/org/applesline/desensitize/executor/DesensitizeExecutor.java create mode 100644 src/main/java/org/applesline/desensitize/executor/DesensitizeExecutorAdapter.java create mode 100644 src/main/java/org/applesline/desensitize/executor/JsonPathDesensitizeExecutor.java create mode 100644 src/main/java/org/applesline/desensitize/handle/DesensitizeAdapter.java create mode 100644 src/main/java/org/applesline/desensitize/handle/DesensitizeHandler.java create mode 100644 src/main/java/org/applesline/desensitize/handle/DesensitizeHandlerSelector.java create mode 100644 src/main/java/org/applesline/desensitize/handle/impl/AddressHandler.java create mode 100644 src/main/java/org/applesline/desensitize/handle/impl/BankCardHandler.java create mode 100644 src/main/java/org/applesline/desensitize/handle/impl/BirthdayHandler.java create mode 100644 src/main/java/org/applesline/desensitize/handle/impl/CarLicenseHandler.java create mode 100644 src/main/resources/application.properties diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8fa5a60 --- /dev/null +++ b/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.2 + + + org.applesline + desensitize + 1.0.0 + desensitize + 数据脱敏工具库 + + 1.8 + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.aspectj + aspectjweaver + + + com.jayway.jsonpath + json-path + 2.2.0 + + + com.google.code.gson + gson + + + + + + + maven-deploy-plugin + 2.8.2 + + + default-deploy + deploy + + deploy + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.5.1 + + + default-deploy + deploy + + deploy + + + + + + + + diff --git a/src/main/java/org/applesline/desensitize/annotation/Desensitize.java b/src/main/java/org/applesline/desensitize/annotation/Desensitize.java new file mode 100644 index 0000000..ef11803 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/annotation/Desensitize.java @@ -0,0 +1,17 @@ +package org.applesline.desensitize.annotation; + +import java.lang.annotation.*; + +/** + * @author liuyaping + * @date 2022/1/13 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Desensitize { + + FieldMapping[] fieldMapping() default {}; + + String[] ignoreByJpe() default {}; +} diff --git a/src/main/java/org/applesline/desensitize/annotation/EnableDesensitize.java b/src/main/java/org/applesline/desensitize/annotation/EnableDesensitize.java new file mode 100644 index 0000000..dd5cc2e --- /dev/null +++ b/src/main/java/org/applesline/desensitize/annotation/EnableDesensitize.java @@ -0,0 +1,27 @@ +package org.applesline.desensitize.annotation; + +import org.applesline.desensitize.config.AutoDesensitizeConfiguration; +import org.applesline.desensitize.config.AutoDesensitizeRegistrar; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +/** + * @author liuyaping + * @date 2022/1/19 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import({AutoDesensitizeConfiguration.class, AutoDesensitizeRegistrar.class}) +public @interface EnableDesensitize { + + FieldMapping[] fieldMapping() default {}; + + /** + * ignore by JsonPath expression language + * + * @return + */ + String[] ignoreByJpe() default {}; +} diff --git a/src/main/java/org/applesline/desensitize/annotation/FieldMapping.java b/src/main/java/org/applesline/desensitize/annotation/FieldMapping.java new file mode 100644 index 0000000..b992150 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/annotation/FieldMapping.java @@ -0,0 +1,15 @@ +package org.applesline.desensitize.annotation; + + +import org.applesline.desensitize.constants.DesensitizeType; + +/** + * @author liuyaping + * @date 2022/1/14 + */ +public @interface FieldMapping { + + DesensitizeType type(); + + String[] fields() default {}; +} diff --git a/src/main/java/org/applesline/desensitize/aop/DesensitizeAdvice.java b/src/main/java/org/applesline/desensitize/aop/DesensitizeAdvice.java new file mode 100644 index 0000000..4d9e209 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/aop/DesensitizeAdvice.java @@ -0,0 +1,69 @@ +package org.applesline.desensitize.aop; + +import org.applesline.desensitize.annotation.Desensitize; +import org.applesline.desensitize.annotation.FieldMapping; +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.executor.DesensitizeExecutor; +import org.applesline.desensitize.executor.JsonPathDesensitizeExecutor; +import org.applesline.desensitize.handle.DesensitizeHandlerSelector; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author liuyaping + * @date 2022/1/14 + */ +@Aspect +public class DesensitizeAdvice implements ApplicationContextAware { + + private DesensitizeExecutor desensitizeExecutor; + private DesensitizeExecutor globalDesensitizeExecutor; + + public DesensitizeAdvice(DesensitizeExecutor desensitizeExecutor) { + this.desensitizeExecutor = desensitizeExecutor; + } + + @Around("@annotation(org.applesline.desensitize.annotation.Desensitize)") + public Object desensitize(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { + Signature signature = proceedingJoinPoint.getSignature(); + Object obj = proceedingJoinPoint.proceed(); + if (signature instanceof MethodSignature) { + MethodSignature methodSignature = (MethodSignature)signature; + Desensitize desensitize = methodSignature.getMethod().getAnnotation(Desensitize.class); + Map fieldMappingMap = new HashMap<>(); + FieldMapping[] fieldMappings = desensitize.fieldMapping(); + for (FieldMapping fieldMapping : fieldMappings) { + DesensitizeType desensitizeType = fieldMapping.type(); + String[] fields = fieldMapping.fields(); + for (String field : fields) { + fieldMappingMap.putIfAbsent(field,desensitizeType); + } + } + if (fieldMappingMap.isEmpty()) { + obj = globalDesensitizeExecutor.executeMask(obj); + } else { + desensitizeExecutor.configFields(fieldMappingMap, Arrays.stream(desensitize.ignoreByJpe()).collect(Collectors.toList())); + obj = desensitizeExecutor.executeMask(obj); + } + } + return obj; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + DesensitizeHandlerSelector selector = applicationContext.getBean(DesensitizeHandlerSelector.class); + this.globalDesensitizeExecutor = new JsonPathDesensitizeExecutor(selector); + this.globalDesensitizeExecutor.configFields(this.desensitizeExecutor.getMaskWordsMap(),this.desensitizeExecutor.getIgnoreJsonPathExpression()); + } +} diff --git a/src/main/java/org/applesline/desensitize/config/AutoDesensitizeConfiguration.java b/src/main/java/org/applesline/desensitize/config/AutoDesensitizeConfiguration.java new file mode 100644 index 0000000..c266930 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/config/AutoDesensitizeConfiguration.java @@ -0,0 +1,32 @@ +package org.applesline.desensitize.config; + +import org.applesline.desensitize.aop.DesensitizeAdvice; +import org.applesline.desensitize.executor.DesensitizeExecutor; +import org.applesline.desensitize.executor.JsonPathDesensitizeExecutor; +import org.applesline.desensitize.handle.DesensitizeHandlerSelector; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author liuyaping + * @date 2022/1/18 + */ +@Configuration +public class AutoDesensitizeConfiguration { + + @Bean + public DesensitizeHandlerSelector maskHandlerSelector() { + return new DesensitizeHandlerSelector(); + } + + @Bean + public DesensitizeExecutor maskSensitiveExecutor(DesensitizeHandlerSelector desensitizeHandlerSelector) { + return new JsonPathDesensitizeExecutor(desensitizeHandlerSelector); + } + + @Bean + public DesensitizeAdvice desensitizeAdvice(DesensitizeExecutor desensitizeExecutor) { + return new DesensitizeAdvice(desensitizeExecutor); + } + +} diff --git a/src/main/java/org/applesline/desensitize/config/AutoDesensitizeRegistrar.java b/src/main/java/org/applesline/desensitize/config/AutoDesensitizeRegistrar.java new file mode 100644 index 0000000..c8ebb5a --- /dev/null +++ b/src/main/java/org/applesline/desensitize/config/AutoDesensitizeRegistrar.java @@ -0,0 +1,68 @@ +package org.applesline.desensitize.config; + +import org.applesline.desensitize.annotation.EnableDesensitize; +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.handle.impl.AddressHandler; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author liuyaping + * @date 2022/1/19 + */ +public class AutoDesensitizeRegistrar implements ImportBeanDefinitionRegistrar { + + private static final String DESENSITIZE_BEAN_POST_PROCESSOR = "desensitizeBeanPostProcessor"; + private static final String FIELD_MAPPING = "fieldMapping"; + private static final String IGNORE_BY_JSONPATH_EXPRESS = "ignoreByJpe"; + private static final String TYPE = "type"; + private static final String FIELDS = "fields"; + + private static final String SCAN_DESENSITIZE_HANDLER_BASE_PACKAGE = AddressHandler.class.getPackage().getName(); + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + Map defaultAttrs = importingClassMetadata.getAnnotationAttributes(EnableDesensitize.class.getName()); + + AnnotationAttributes[] annotationAttributes = (AnnotationAttributes[]) defaultAttrs.get(FIELD_MAPPING); + Map fieldMappingMap = new HashMap<>(); + for (AnnotationAttributes annotationAttribute : annotationAttributes) { + String[] fields = (String[])annotationAttribute.get(FIELDS); + DesensitizeType desensitizeType = (DesensitizeType)annotationAttribute.get(TYPE); + for (String field : fields) { + fieldMappingMap.putIfAbsent(field,desensitizeType); + } + } + String[] ignoreByJsonPathExpress = (String[])defaultAttrs.get(IGNORE_BY_JSONPATH_EXPRESS); + List jpeList = new ArrayList<>(); + for (String str : ignoreByJsonPathExpress) { + if (!StringUtils.isEmpty(str.trim())) { + jpeList.add(str.trim()); + } + } + + if (!registry.containsBeanDefinition(DESENSITIZE_BEAN_POST_PROCESSOR)) { + AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder + .genericBeanDefinition(DesensitizeBeanPostProcessor.class) + .addConstructorArgValue(fieldMappingMap) + .addConstructorArgValue(jpeList) + .getBeanDefinition(); + registry.registerBeanDefinition(DESENSITIZE_BEAN_POST_PROCESSOR, beanDefinition); + } + + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry); + scanner.scan(SCAN_DESENSITIZE_HANDLER_BASE_PACKAGE); + } + +} diff --git a/src/main/java/org/applesline/desensitize/config/DesensitizeBeanPostProcessor.java b/src/main/java/org/applesline/desensitize/config/DesensitizeBeanPostProcessor.java new file mode 100644 index 0000000..a30a0a6 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/config/DesensitizeBeanPostProcessor.java @@ -0,0 +1,38 @@ +package org.applesline.desensitize.config; + +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.executor.JsonPathDesensitizeExecutor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +import java.util.List; +import java.util.Map; + +/** + * @author liuyaping + * @date 2022/1/20 + */ +public class DesensitizeBeanPostProcessor implements BeanPostProcessor { + + Map maskFieldMapping; + List ignoreByJsonPathExpress; + + public DesensitizeBeanPostProcessor(Map maskFieldMapping, List ignoreByJsonPathExpress) { + this.maskFieldMapping = maskFieldMapping; + this.ignoreByJsonPathExpress = ignoreByJsonPathExpress; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean.getClass() == JsonPathDesensitizeExecutor.class) { + JsonPathDesensitizeExecutor executor = (JsonPathDesensitizeExecutor)bean; + executor.configFields(maskFieldMapping,ignoreByJsonPathExpress); + } + return bean; + } +} diff --git a/src/main/java/org/applesline/desensitize/constants/DesensitizeType.java b/src/main/java/org/applesline/desensitize/constants/DesensitizeType.java new file mode 100644 index 0000000..8bb0bf8 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/constants/DesensitizeType.java @@ -0,0 +1,22 @@ +package org.applesline.desensitize.constants; + +/** + * @author liuyaping + * @date 2022/1/14 + */ +public enum DesensitizeType { + + EMAIL, + MOBILE, + PASSWORD, + USER_ID, + IP, + ADDRESS, + OTHER_CARD, + BANK_CARD, + CAR_LICENSE, + GENDER, + BIRTHDAY, + CHINESE_NAME, + ID_CARD; +} diff --git a/src/main/java/org/applesline/desensitize/executor/DesensitizeExecutor.java b/src/main/java/org/applesline/desensitize/executor/DesensitizeExecutor.java new file mode 100644 index 0000000..05c9062 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/executor/DesensitizeExecutor.java @@ -0,0 +1,23 @@ +package org.applesline.desensitize.executor; + + +import org.applesline.desensitize.constants.DesensitizeType; + +import java.util.Collection; +import java.util.Map; + +/** + * @author liuyaping + * @date 2022/1/13 + */ +public interface DesensitizeExecutor { + + void configFields(Map maskWordsMap, Collection ignoreJsonPathExpression); + + Map getMaskWordsMap(); + + Collection getIgnoreJsonPathExpression(); + + Object executeMask(Object obj); + +} diff --git a/src/main/java/org/applesline/desensitize/executor/DesensitizeExecutorAdapter.java b/src/main/java/org/applesline/desensitize/executor/DesensitizeExecutorAdapter.java new file mode 100644 index 0000000..7c8e649 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/executor/DesensitizeExecutorAdapter.java @@ -0,0 +1,57 @@ +package org.applesline.desensitize.executor; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.handle.DesensitizeHandlerSelector; + +import java.util.Collection; +import java.util.Map; + +/** + * @author liuyaping + * @date 2022/1/17 + */ +public class DesensitizeExecutorAdapter implements DesensitizeExecutor { + + protected DesensitizeHandlerSelector desensitizeHandlerSelector; + + protected Map maskWordsMap; + protected Collection ignoreJsonPathExpression; + + public DesensitizeExecutorAdapter(DesensitizeHandlerSelector desensitizeHandlerSelector) { + this.desensitizeHandlerSelector = desensitizeHandlerSelector; + } + + protected Gson gson = new GsonBuilder().create(); + + @Override + public void configFields(Map maskWordsMap, Collection ignoreJsonPathExpression) { + this.maskWordsMap = maskWordsMap; + this.ignoreJsonPathExpression = ignoreJsonPathExpression; + } + + @Override + public Map getMaskWordsMap() { + return this.maskWordsMap; + } + + @Override + public Collection getIgnoreJsonPathExpression() { + return this.ignoreJsonPathExpression; + } + + @Override + public Object executeMask(Object obj) { + throw new RuntimeException(); + } + + public Long doMask(DesensitizeType desensitizeType) { + return desensitizeHandlerSelector.getService(desensitizeType).doMask(); + } + + public String doMask(DesensitizeType desensitizeType, String origin) { + return desensitizeHandlerSelector.getService(desensitizeType).doMask(origin); + } + +} diff --git a/src/main/java/org/applesline/desensitize/executor/JsonPathDesensitizeExecutor.java b/src/main/java/org/applesline/desensitize/executor/JsonPathDesensitizeExecutor.java new file mode 100644 index 0000000..3b7cd7c --- /dev/null +++ b/src/main/java/org/applesline/desensitize/executor/JsonPathDesensitizeExecutor.java @@ -0,0 +1,73 @@ +package org.applesline.desensitize.executor; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.jayway.jsonpath.JsonPath; +import org.applesline.desensitize.handle.DesensitizeHandlerSelector; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + + +/** + * @author liuyaping + * @date 2022/1/14 + */ +public class JsonPathDesensitizeExecutor extends DesensitizeExecutorAdapter { + + public JsonPathDesensitizeExecutor(DesensitizeHandlerSelector desensitizeHandlerSelector) { + super(desensitizeHandlerSelector); + } + + @Override + public Object executeMask(Object obj) { + JsonElement jsonTree = gson.toJsonTree(obj); + maskSensitiveWords(jsonTree,matchIgnoreFields(obj)); + return gson.fromJson(jsonTree.toString(),obj.getClass()); + } + + private void maskSensitiveWords(JsonElement originElement,Collection ignoreMaskWords) { + Set needMaskWords = maskWordsMap.keySet(); + if (originElement.isJsonArray()) { + JsonArray jsonArray = originElement.getAsJsonArray(); + for (JsonElement jsonElement : jsonArray) { + maskSensitiveWords(jsonElement,ignoreMaskWords); + } + } else if (originElement.isJsonObject()) { + JsonObject jsonObject = originElement.getAsJsonObject(); + for (Map.Entry entry : originElement.getAsJsonObject().entrySet()) { + JsonElement element = entry.getValue(); + if (element.isJsonArray()) { + maskSensitiveWords(element.getAsJsonArray(),ignoreMaskWords); + } else if (element.isJsonObject()) { + maskSensitiveWords(element.getAsJsonObject(),ignoreMaskWords); + } else { + if (needMaskWords.contains(entry.getKey()) && !ignoreMaskWords.contains(entry.getValue().getAsString())) { + if (element.getAsJsonPrimitive().isNumber()) { + jsonObject.addProperty(entry.getKey(), doMask(maskWordsMap.get(entry.getKey()))); + } else { + jsonObject.addProperty(entry.getKey(), doMask(maskWordsMap.get(entry.getKey()),entry.getValue().getAsString())); + } + } + } + } + } + } + + private Collection matchIgnoreFields(Object obj) { + Collection ignoreMaskFields = new HashSet<>(); + for (String ignoreJpe : ignoreJsonPathExpression) { + Object field = JsonPath.read(gson.toJsonTree(obj).toString(),ignoreJpe); + if (field instanceof Collection) { + ignoreMaskFields.addAll((Collection) field); + } else { + ignoreMaskFields.add(String.valueOf(field)); + } + } + return ignoreMaskFields; + } + +} diff --git a/src/main/java/org/applesline/desensitize/handle/DesensitizeAdapter.java b/src/main/java/org/applesline/desensitize/handle/DesensitizeAdapter.java new file mode 100644 index 0000000..6b8bab5 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/DesensitizeAdapter.java @@ -0,0 +1,131 @@ +package org.applesline.desensitize.handle; + +import org.applesline.desensitize.constants.DesensitizeType; + +/** + * 屏蔽敏感字段处理器。 + * + * @author liuyaping + * @date 2022/1/14 + */ +public class DesensitizeAdapter implements DesensitizeHandler { + + @Override + public String doMask(String fieldValue) { + throw new RuntimeException(); + } + + @Override + public Long doMask() { + return 0L; + } + + protected String maskCardNumber(String idCardNum,int front,int end) { + if (!isBlank(idCardNum) && front + end > idCardNum.length()) { + return front >= 0 && end >= 0 ? hide(idCardNum, front, idCardNum.length() - end) : ""; + } + return ""; + } + + protected String hide(CharSequence str, int startInclude, int endExclude) { + return replace(str, startInclude, endExclude, '*'); + } + + protected String replace(CharSequence str, int startInclude, int endExclude, char replacedChar) { + if (isEmpty(str)) { + return str(str); + } else { + int strLength = str.length(); + if (startInclude > strLength) { + return str(str); + } else { + if (endExclude > strLength) { + endExclude = strLength; + } + + if (startInclude > endExclude) { + return str(str); + } else { + char[] chars = new char[strLength]; + + for(int i = 0; i < strLength; ++i) { + if (i >= startInclude && i < endExclude) { + chars[i] = replacedChar; + } else { + chars[i] = str.charAt(i); + } + } + + return new String(chars); + } + } + } + } + + protected int indexOf(CharSequence seq, int searchChar) { + return isEmpty(seq) ? -1 : indexOf(seq, searchChar, 0); + } + + private int indexOf(final CharSequence cs, final int searchChar, int start) { + if (cs instanceof String) { + return ((String)cs).indexOf(searchChar, start); + } else { + int sz = cs.length(); + if (start < 0) { + start = 0; + } + + for(int i = start; i < sz; ++i) { + if (cs.charAt(i) == searchChar) { + return i; + } + } + + return -1; + } + } + + private boolean isEmpty(CharSequence cs) { + return cs == null || cs.length() == 0; + } + + protected String str(CharSequence cs) { + return cs == null ? null : cs.toString(); + } + + protected String repeat(char c, int count) { + if (count <= 0) { + return ""; + } else { + char[] result = new char[count]; + + for(int i = 0; i < count; ++i) { + result[i] = c; + } + return new String(result); + } + } + + protected boolean isBlank(CharSequence cs) { + int strLen; + if (cs != null && (strLen = cs.length()) != 0) { + for(int i = 0; i < strLen; ++i) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } else { + return true; + } + } + + public static String trim(String str) { + return str == null ? null : str.trim(); + } + + @Override + public DesensitizeType getFieldType() { + throw new RuntimeException(); + } +} diff --git a/src/main/java/org/applesline/desensitize/handle/DesensitizeHandler.java b/src/main/java/org/applesline/desensitize/handle/DesensitizeHandler.java new file mode 100644 index 0000000..80b0c62 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/DesensitizeHandler.java @@ -0,0 +1,18 @@ +package org.applesline.desensitize.handle; + +import org.applesline.desensitize.constants.DesensitizeType; + +/** + * 屏蔽敏感字段处理器。 + * + * @author liuyaping + * @date 2022/1/14 + */ +public interface DesensitizeHandler { + + String doMask(String fieldValue); + + Long doMask(); + + DesensitizeType getFieldType(); +} diff --git a/src/main/java/org/applesline/desensitize/handle/DesensitizeHandlerSelector.java b/src/main/java/org/applesline/desensitize/handle/DesensitizeHandlerSelector.java new file mode 100644 index 0000000..a630669 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/DesensitizeHandlerSelector.java @@ -0,0 +1,30 @@ +package org.applesline.desensitize.handle; + +import org.applesline.desensitize.constants.DesensitizeType; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author liuyaping + * @date 2022/1/14 + */ +public class DesensitizeHandlerSelector implements ApplicationContextAware { + + private Map serviceMap = new HashMap<>(); + + public DesensitizeHandler getService(DesensitizeType desensitizeType) { + return serviceMap.get(desensitizeType); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + Map handlers = applicationContext.getBeansOfType(DesensitizeHandler.class); + handlers.values().forEach(handler->{ + serviceMap.putIfAbsent(handler.getFieldType(),handler); + }); + } +} diff --git a/src/main/java/org/applesline/desensitize/handle/impl/AddressHandler.java b/src/main/java/org/applesline/desensitize/handle/impl/AddressHandler.java new file mode 100644 index 0000000..7dae2ea --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/impl/AddressHandler.java @@ -0,0 +1,28 @@ +package org.applesline.desensitize.handle.impl; + +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.handle.DesensitizeAdapter; +import org.springframework.stereotype.Component; + +/** + * @author liuyaping + * @date 2022/1/13 + */ +@Component +public class AddressHandler extends DesensitizeAdapter { + + @Override + public String doMask(String address) { + if (isBlank(address)) { + return ""; + } else { + int length = address.length(); + return hide(address, length - 8, length); + } + } + + @Override + public DesensitizeType getFieldType() { + return DesensitizeType.ADDRESS; + } +} diff --git a/src/main/java/org/applesline/desensitize/handle/impl/BankCardHandler.java b/src/main/java/org/applesline/desensitize/handle/impl/BankCardHandler.java new file mode 100644 index 0000000..05e1f06 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/impl/BankCardHandler.java @@ -0,0 +1,46 @@ +package org.applesline.desensitize.handle.impl; + +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.handle.DesensitizeAdapter; +import org.springframework.stereotype.Component; + +/** + * @author liuyaping + * @date 2022/1/13 + */ +@Component +public class BankCardHandler extends DesensitizeAdapter { + + @Override + public String doMask(String bankCardNo) { + if (isBlank(bankCardNo)) { + return bankCardNo; + } else { + bankCardNo = trim(bankCardNo); + if (bankCardNo.length() < 9) { + return bankCardNo; + } else { + int length = bankCardNo.length(); + int midLength = length - 8; + StringBuilder buf = new StringBuilder(); + buf.append(bankCardNo, 0, 4); + + for(int i = 0; i < midLength; ++i) { + if (i % 4 == 0) { + buf.append(" "); + } + + buf.append('*'); + } + + buf.append(" ").append(bankCardNo, length - 4, length); + return buf.toString(); + } + } + } + + @Override + public DesensitizeType getFieldType() { + return DesensitizeType.BANK_CARD; + } +} diff --git a/src/main/java/org/applesline/desensitize/handle/impl/BirthdayHandler.java b/src/main/java/org/applesline/desensitize/handle/impl/BirthdayHandler.java new file mode 100644 index 0000000..58b08a9 --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/impl/BirthdayHandler.java @@ -0,0 +1,22 @@ +package org.applesline.desensitize.handle.impl; + +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.handle.DesensitizeAdapter; +import org.springframework.stereotype.Component; + +/** + * @author liuyaping + * @date 2022/1/19 + */ +@Component +public class BirthdayHandler extends DesensitizeAdapter { + @Override + public String doMask(String birthday) { + return isBlank(birthday) ? "" : "****-**-**"; + } + + @Override + public DesensitizeType getFieldType() { + return DesensitizeType.BIRTHDAY; + } +} diff --git a/src/main/java/org/applesline/desensitize/handle/impl/CarLicenseHandler.java b/src/main/java/org/applesline/desensitize/handle/impl/CarLicenseHandler.java new file mode 100644 index 0000000..eaa8c1e --- /dev/null +++ b/src/main/java/org/applesline/desensitize/handle/impl/CarLicenseHandler.java @@ -0,0 +1,32 @@ +package org.applesline.desensitize.handle.impl; + +import org.applesline.desensitize.constants.DesensitizeType; +import org.applesline.desensitize.handle.DesensitizeAdapter; +import org.springframework.stereotype.Component; + +/** + * @author liuyaping + * @date 2022/1/19 + */ +@Component +public class CarLicenseHandler extends DesensitizeAdapter { + @Override + public String doMask(String carLicense) { + if (isBlank(carLicense)) { + return ""; + } else { + if (carLicense.length() == 7) { + carLicense = hide(carLicense, 3, 6); + } else if (carLicense.length() == 8) { + carLicense = hide(carLicense, 3, 7); + } + + return carLicense; + } + } + + @Override + public DesensitizeType getFieldType() { + return DesensitizeType.CAR_LICENSE; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +