使用十一种设计模式实现 基于Jackson注解的 完美数据脱敏方案
@UtilityClass
class SensitiveDeals {
String none(String source, Sensitive sensitive) {
char hidedChar = sensitive.replacement().charAt(0);
char[] chars = new char[source.length()];
Arrays.fill(chars, hidedChar);
return new String(chars);
}
SensitiveDeal createSuffix(int showLength) {
return (source, sensitive) -> {
char hidedChar = sensitive.replacement().charAt(0);
return suffix0(source, showLength, hidedChar);
};
}
String suffix(String source, Sensitive sensitive) {
char hidedChar = sensitive.replacement().charAt(0);
return suffix0(source, sensitive.keepTail(), hidedChar);
}
SensitiveDeal createPrefix(int showLength) {
return (source, sensitive) -> {
char hidedChar = sensitive.replacement().charAt(0);
return prefix0(source, showLength, hidedChar);
};
}
String prefix(String source, Sensitive sensitive) {
char hidedChar = sensitive.replacement().charAt(0);
return prefix0(source, sensitive.maskStart(), hidedChar);
}
SensitiveDeal createWrap(final int maskStart, final int keepTail) {
return (source, sensitive) -> {
char hidedChar = sensitive.replacement().charAt(0);
return wrap0(source, maskStart, keepTail, hidedChar);
};
}
String wrap(String source, Sensitive sensitive) {
char hidedChar = sensitive.replacement().charAt(0);
return wrap0(source, sensitive.maskStart(), sensitive.keepTail(), hidedChar);
}
/**
* Pattern 是线程安全的,所以缓存起来。<br>
* 实例数量不会有太多,暂时不需要使用复杂缓存策略。
*/
Map<String, Pattern> regularCache = new ConcurrentHashMap<>();
SensitiveDeal createRegular(String regular, String replacement) {
Assert.notBlank(regular);
Pattern pattern = regularCache.computeIfAbsent(regular, Pattern::compile);
return (source, sensitive) -> {
return regular0(source, pattern, replacement);
};
}
String regular(String source, Sensitive sensitive) {
Assert.notBlank(sensitive.regular());
Assert.notBlank(sensitive.replacement());
Pattern pattern = regularCache.computeIfAbsent(sensitive.regular(), Pattern::compile);
return regular0(source, pattern, sensitive.replacement());
}
private String suffix0(final String source, final int showLength, final char hidedChar) {
if (StringUtils.length(source) <= showLength) {
return source;
}
char[] chars = new char[source.length()];
Arrays.fill(chars, hidedChar);
int begin = source.length() - showLength;
source.getChars(begin, source.length(), chars, begin);
return new String(chars);
}
private String prefix0(final String source, final int showLength, final char hidedChar) {
if (StringUtils.length(source) <= showLength) {
return source;
}
char[] chars = new char[source.length()];
Arrays.fill(chars, hidedChar);
source.getChars(0, showLength, chars, 0);
return new String(chars);
}
private String wrap0(final String source, final int maskStart, final int keepTail, final char hidedChar) {
if (source.length() <= maskStart + keepTail + 1) {
char[] chars = source.toCharArray();
chars[source.length() / 2] = hidedChar;
return new String(chars);
}
char[] chars = new char[source.length()];
Arrays.fill(chars, hidedChar);
source.getChars(0, maskStart, chars, 0);
int begin = source.length() - keepTail;
source.getChars(begin, source.length(), chars, begin);
return new String(chars);
}
private String regular0(final String source, final Pattern pattern, final String replacement) {
Matcher matcher = pattern.matcher(source);
if (!matcher.matches()) {
return source;
}
return matcher.replaceAll(replacement);
}
/**
* 自定义脱敏的实例。肯定线程安全。。<br>
* 实例数量不会有太多,暂时不需要使用复杂缓存策略。
*/
Map<Class<? extends SensitiveDeal>, SensitiveDeal> customerCache = new ConcurrentHashMap<>();
String customerCommon(String source, Sensitive sensitive) {
Class<? extends SensitiveDeal> clz = sensitive.dealType();
Objects.requireNonNull(clz);
SensitiveDeal deal = customerCache.computeIfAbsent(clz, cls -> {
try {
return cls.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException("脱敏策略无法实例化:" + cls.getName(), e);
}
});
return deal.apply(source, sensitive);
}
String userName(String source, Sensitive sensitive) {
if (StringUtils.length(source) < 2) {
return source;
}
char hidedChar = sensitive.replacement().charAt(0);
if (source.length() < 5) {
char[] chars = source.toCharArray();
chars[1] = hidedChar;
return new String(chars);
}
char[] chars = new char[source.length()];
Arrays.fill(chars, hidedChar);
source.getChars(0, 1, chars, 0);
int begin = source.length() - 2;
source.getChars(begin, source.length(), chars, begin);
return new String(chars);
}
String email(String source, Sensitive sensitive) {
int index = StringUtils.indexOf(source, "@");
if (index <= 0) {
return source;
}
char hidedChar = sensitive.replacement().charAt(0);
String acc = source.substring(0, index);
String host = source.substring(index);
return wrap0(acc, 2, 1, hidedChar) + host;
}
}