From 11f54f266bf138894787dbd65b175136e8138bed Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 21 Mar 2021 15:13:57 +0800 Subject: [PATCH] add DesensitizedUtil --- CHANGELOG.md | 3 +- .../cn/hutool/core/text/CharSequenceUtil.java | 451 +----------------- .../cn/hutool/core/util/DesensitizedUtil.java | 217 +++++++++ .../hutool/core/util/DesensitizedUtils.java | 154 ------ .../core/util/DesensitizedUtilTest.java | 79 +++ .../core/util/DesensitizedUtilsTest.java | 58 --- .../java/cn/hutool/core/util/StrUtilTest.java | 96 +--- 7 files changed, 323 insertions(+), 735 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtil.java delete mode 100644 hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtils.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilTest.java delete mode 100644 hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilsTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3066551a7..73018e528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ ------------------------------------------------------------------------------------------------------------- -# 5.6.2 (2021-03-20) +# 5.6.2 (2021-03-21) ### 新特性 * 【core 】 Validator增加车架号(车辆识别码)验证、驾驶证(驾驶证档案编号)的正则校验(pr#280@Gitee) * 【core 】 CopyOptions增加propertiesFilter(pr#281@Gitee) * 【extra 】 增加Wit模板引擎支持 +* 【core 】 增加DesensitizedUtil(pr#282@Gitee) ### Bug修复 ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java index 5f5b5bd3f..780a06d1f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java @@ -5,7 +5,13 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.func.Func1; -import cn.hutool.core.util.*; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -2976,9 +2982,9 @@ public class CharSequenceUtil { * StrUtil.padPre("1039", -1, "0");//"103" * * - * @param str 字符串 - * @param length 长度 - * @param padStr 补充的字符 + * @param str 字符串 + * @param length 长度 + * @param padStr 补充的字符 * @return 补充后的字符串 */ public static String padPre(CharSequence str, int length, CharSequence padStr) { @@ -3006,9 +3012,9 @@ public class CharSequenceUtil { * StrUtil.padPre("123", 2, '0');//"12" * * - * @param str 字符串 - * @param length 长度 - * @param padChar 补充的字符 + * @param str 字符串 + * @param length 长度 + * @param padChar 补充的字符 * @return 补充后的字符串 */ public static String padPre(CharSequence str, int length, char padChar) { @@ -3036,9 +3042,9 @@ public class CharSequenceUtil { * StrUtil.padAfter("123", -1, '0')//"" 空串 * * - * @param str 字符串,如果为{@code null},直接返回null - * @param length 长度 - * @param padChar 补充的字符 + * @param str 字符串,如果为{@code null},直接返回null + * @param length 长度 + * @param padChar 补充的字符 * @return 补充后的字符串 */ public static String padAfter(CharSequence str, int length, char padChar) { @@ -3065,9 +3071,9 @@ public class CharSequenceUtil { * StrUtil.padAfter("123", 2, "ABC");//"23" * * - * @param str 字符串,如果为{@code null},直接返回null - * @param length 长度 - * @param padStr 补充的字符 + * @param str 字符串,如果为{@code null},直接返回null + * @param length 长度 + * @param padStr 补充的字符 * @return 补充后的字符串 * @since 4.3.2 */ @@ -3629,61 +3635,6 @@ public class CharSequenceUtil { return replace(str, startInclude, endExclude, '*'); } - /** - * 脱敏,使用默认的脱敏策略 - *
-	 * StrUtil.hide("100", DesensitizedUtils.DesensitizedType.USER_ID)) =  "0"
-	 * StrUtil.hide("段正淳", DesensitizedUtils.DesensitizedType.CHINESE_NAME)) = "段**"
-	 * StrUtil.hide("51343620000320711X", DesensitizedUtils.DesensitizedType.ID_CARD)) = "5***************1X"
-	 * StrUtil.hide("09157518479", DesensitizedUtils.DesensitizedType.FIXED_PHONE)) = "0915*****79"
-	 * StrUtil.hide("18049531999", DesensitizedUtils.DesensitizedType.MOBILE_PHONE)) = "180****1999"
-	 * StrUtil.hide("北京市海淀区马连洼街道289号", DesensitizedUtils.DesensitizedType.ADDRESS)) = "北京市海淀区马********"
-	 * StrUtil.hide("duandazhi-jack@gmail.com.cn", DesensitizedUtils.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn"
-	 * StrUtil.hide("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)) = "**********"
-	 * 
- * - * @author dazer and neusoft and qiaomu - * @see DesensitizedUtils 如果需要自定义,脱敏规则,请使用该工具类; - * @param str 字符串 - * @param desensitizedType 脱敏类型;可以脱敏:用户id、中文名、身份证号、座机号、手机号、地址、电子邮件、密码 - * @return 脱敏之后的字符串 - * @since 5.6.2 - */ - public static String hide(CharSequence str, DesensitizedUtils.DesensitizedType desensitizedType) { - if (isBlank(str)) { - return EMPTY; - } - String newStr = String.valueOf(str); - switch (desensitizedType) { - case USER_ID: - newStr = String.valueOf(DesensitizedUtils.userId()); - break; - case CHINESE_NAME: - newStr = DesensitizedUtils.chineseName(String.valueOf(str)); - break; - case ID_CARD: - newStr = DesensitizedUtils.idCardNum(String.valueOf(str),1,2); - break; - case FIXED_PHONE: - newStr = DesensitizedUtils.fixedPhone(String.valueOf(str)); - break; - case MOBILE_PHONE: - newStr = DesensitizedUtils.mobilePhone(String.valueOf(str)); - break; - case ADDRESS: - newStr = DesensitizedUtils.address(String.valueOf(str), 8); - break; - case EMAIL: - newStr = DesensitizedUtils.email(String.valueOf(str)); - break; - case PASSWORD: - newStr = DesensitizedUtils.password(String.valueOf(str)); - break; - default: - } - return newStr; - } - /** * 脱敏,使用默认的脱敏策略 * @@ -3698,15 +3649,15 @@ public class CharSequenceUtil { * StrUtil.desensitized("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)) = "**********" * * - * @author dazer and neusoft and qiaomu - * @see DesensitizedUtils 如果需要自定义,脱敏规则,请使用该工具类; - * @param str 字符串 + * @param str 字符串 * @param desensitizedType 脱敏类型;可以脱敏:用户id、中文名、身份证号、座机号、手机号、地址、电子邮件、密码 * @return 脱敏之后的字符串 + * @author dazer and neusoft and qiaomu + * @see DesensitizedUtil 如果需要自定义,脱敏规则,请使用该工具类; * @since 5.6.2 */ - public static String desensitized(CharSequence str, DesensitizedUtils.DesensitizedType desensitizedType) { - return hide(str, desensitizedType); + public static String desensitized(CharSequence str, DesensitizedUtil.DesensitizedType desensitizedType) { + return DesensitizedUtil.desensitized(str, desensitizedType); } /** @@ -4357,358 +4308,4 @@ public class CharSequenceUtil { return strBuilder.toString(); } - // Left/Right/Mid 用来兼容 org.apache.commons.lang3.StringUtils 使用习惯 start - //------------------------------------------------------------------------- - /** - *

获取字符串的最左{@code len}个字符。

- * - *

如果{@code len}个字符不可用,或者字符串为{@code null}, - * 则将无例外地返回该字符串。 - * 如果len为负,则返回一个空字符串。 - *

- * - *

-	 * StrUtil.left(null, *)    = null
-	 * StrUtil.left(*, -ve)     = ""
-	 * StrUtil.left("", *)      = ""
-	 * StrUtil.left("neu", 0)   = ""
-	 * StrUtil.left("neu", 2)   = "ne"
-	 * StrUtil.left("neu", 4)   = "neu"
-	 * 
- * - * @param str 从中获取最左边字符的字符串,可以为null - * @param len 要求的字符串长度 - * @return 返回最左边的字符串 - * @since 5.6.2 - */ - public static String left(final String str, final int len) { - if (str == null) { - return null; - } - if (len < 0) { - return EMPTY; - } - if (str.length() <= len) { - return str; - } - return str.substring(0, len); - } - - /** - *

用空格在左侧填充字符串 (' ').

- * - *

填充字符串到指定的长度 {@code size}.

- * - *
-	 * StrUtil.leftPad(null, *)   = null
-	 * StrUtil.leftPad("", 3)     = "   "
-	 * StrUtil.leftPad("bat", 1)  = "bat"
-	 * StrUtil.leftPad("bat", -1) = "bat"
-	 * StrUtil.leftPad("bat", 3)  = "bat"
-	 * StrUtil.leftPad("bat", 5)  = "  bat"
-	 *
-	 * 
- * - * @param str 要填充的字符串,可以为null - * @param size 填充到size长度 - * @return 如果不需要填充,请使用左填充字符串或原始字符串, - * {@code null} if null String input - * @since 5.6.2 - */ - public static String leftPad(final String str, final int size) { - return leftPad(str, size, ' '); - } - - /** - *

Left pad a String with a specified character.

- *

左侧用指定的字符串进行填充.

- * - *

Pad to a size of {@code size}.

- *

填充到指定的长度 {@code size}.

- * - *
-	 * StrUtil.leftPad(null, *, *)     = null
-	 * StrUtil.leftPad("", 3, 'z')     = "zzz"
-	 * StrUtil.leftPad("bat", -1, 'z') = "bat"
-	 * StrUtil.leftPad("bat", 1, 'z')  = "bat"
-	 * StrUtil.leftPad("bat", 3, 'z')  = "bat"
-	 * StrUtil.leftPad("bat", 5, 'z')  = "zzbat"
-	 *
-	 * 
- * - * @param str the String to pad out, may be null; 要填充的字符串,可以为null - * @param size the size to pad to; 填充到size长度 - * @param padChar the character to pad with; 用来填充的字符 - * @return left padded String or original String if no padding is necessary, - * 左侧填充字符串 or 如果不需要填充则返回原始字符串; - * {@code null} if null String input; 如果输入字符串是null - * @since 5.6.2 - */ - public static String leftPad(final String str, final int size, final char padChar) { - if (str == null) { - return null; - } - final int pads = size - str.length(); - if (pads <= 0) { - return str; // 尽可能返回原始字符串 returns original String when possible - } - if (pads > PAD_LIMIT) { - return leftPad(str, size, String.valueOf(padChar)); - } - return repeat(padChar, pads).concat(str); - } - - /** - *

用指定的字符串左填充字符串.

- * - *

填充到指定的长度 {@code size}.

- * - *
-	 * StrUtil.leftPad(null, *, *)      = null
-	 * StrUtil.leftPad("", 3, "z")      = "zzz"
-	 * StrUtil.leftPad("bat", 3, "yz")  = "bat"
-	 * StrUtil.leftPad("bat", 5, "yz")  = "yzbat"
-	 * StrUtil.leftPad("bat", 8, "yz")  = "yzyzybat"
-	 * StrUtil.leftPad("bat", 1, "yz")  = "bat"
-	 * StrUtil.leftPad("bat", -1, "yz") = "bat"
-	 * StrUtil.leftPad("bat", 5, null)  = "  bat"
-	 * StrUtil.leftPad("bat", 5, "")    = "  bat"
-	 * 
- * - * @param str 要填充的字符串,可以为null - * @param size 填充到指定的长度 - * @param padStr 要填充的字符串,如果为null或空,则将其视为单个空格 - * @return 如果不需要填充,请使用左填充字符串或原始字符串, - * {@code null} 如果为null字符串输入 - * @since 5.6.2 - */ - public static String leftPad(final String str, final int size, String padStr) { - if (str == null) { - return null; - } - if (isEmpty(padStr)) { - padStr = SPACE; - } - final int padLen = padStr.length(); - final int strLen = str.length(); - final int pads = size - strLen; - if (pads <= 0) { - return str; // 当有可能返回原始字符串; returns original String when possible - } - if (padLen == 1 && pads <= PAD_LIMIT) { - return leftPad(str, size, padStr.charAt(0)); - } - - if (pads == padLen) { - return padStr.concat(str); - } else if (pads < padLen) { - return padStr.substring(0, pads).concat(str); - } else { - final char[] padding = new char[pads]; - final char[] padChars = padStr.toCharArray(); - for (int i = 0; i < pads; i++) { - padding[i] = padChars[i % padLen]; - } - return new String(padding).concat(str); - } - } - - /** - *

获取字符串的最右边{@code len}个字符。

- * - *

如果{@code len}个字符不可用,或者字符串为{@code null},则将无例外地返回该字符串。如果len为负,则返回一个空字符串。

- * - *

-	 * StrUtil.right(null, *)    = null
-	 * StrUtil.right(*, -ve)     = ""
-	 * StrUtil.right("", *)      = ""
-	 * StrUtil.right("abc", -1)   = ""
-	 * StrUtil.right("abc", 0)   = ""
-	 * StrUtil.right("abc", 2)   = "bc"
-	 * StrUtil.right("abc", 4)   = "abc"
-	 * 
- * - * @param str 从中获取最右边字符的字符串,可以为null - * @param len 所需字符串的长度 - * @return 最右边的字符,{@code null},如果为null字符串输入 - * @since 5.6.2 - */ - public static String right(final String str, final int len) { - if (str == null) { - return null; - } - if (len < 0) { - return EMPTY; - } - if (str.length() <= len) { - return str; - } - return str.substring(str.length() - len); - } - - /** - *

用空格('')右填充字符串。

- *

将字符串填充为{@code size}的大小。

- * - *

-	 * StrUtil.rightPad(null, *)   = null
-	 * StrUtil.rightPad("", 3)     = "   "
-	 * StrUtil.rightPad("bat", 3)  = "bat"
-	 * StrUtil.rightPad("bat", 5)  = "bat  "
-	 * StrUtil.rightPad("bat", 1)  = "bat"
-	 * StrUtil.rightPad("bat", -1) = "bat"
-	 * 
- * - * @param str 要填充的字符串,可以为null - * @param size 填充的长度 - * @return 如果不需要填充,则使用右填充的字符串或原始字符串, - * {@code null} 如果为null字符串输入 - * @since 5.6.2 - */ - public static String rightPad(final String str, final int size) { - return rightPad(str, size, ' '); - } - - /** - *

用指定的字符填充到原字符串的右侧.

- * - *

填充字符串到几位 {@code size}.

- * - *
-	 * StrUtil.rightPad(null, *, *)     = null
-	 * StrUtil.rightPad("", 3, 'z')     = "zzz"
-	 * StrUtil.rightPad("bat", 3, 'z')  = "bat"
-	 * StrUtil.rightPad("bat", 5, 'z')  = "batzz"
-	 * StrUtil.rightPad("bat", 1, 'z')  = "bat"
-	 * StrUtil.rightPad("bat", -1, 'z') = "bat"
-	 * 
- * - * @param str 要填充的字符串,可以为null - * @param size 要填充的到size位 - * @param padChar 用来填充的字符串 - * @return 返回 右侧填充字符串 or 如果不需要填充,则为原始String。 - * {@code null} 如果为null字符串输入 - * @since 5.6.2 - */ - public static String rightPad(final String str, final int size, final char padChar) { - if (str == null) { - return null; - } - final int pads = size - str.length(); - if (pads <= 0) { - return str; // 当有可能返回原始字符串; returns original String when possible - } - if (pads > PAD_LIMIT) { - return rightPad(str, size, String.valueOf(padChar)); - } - return str.concat(repeat(padChar, pads)); - } - - /** - *

Right pad a String with a specified String.

- *

用自定的字符串进行右侧填充.

- * - *

The String is padded to the size of {@code size}.

- *

填充字符串到指定长度{@code size}.

- * - *
-	 * StrUtil.rightPad(null, *, *)      = null
-	 * StrUtil.rightPad("", 3, "z")      = "zzz"
-	 * StrUtil.rightPad("bat", 3, "yz")  = "bat"
-	 * StrUtil.rightPad("bat", 5, "yz")  = "batyz"
-	 * StrUtil.rightPad("bat", 8, "yz")  = "batyzyzy"
-	 * StrUtil.rightPad("bat", 1, "yz")  = "bat"
-	 * StrUtil.rightPad("bat", -1, "yz") = "bat"
-	 * StrUtil.rightPad("bat", 5, null)  = "bat  "
-	 * StrUtil.rightPad("bat", 5, "")    = "bat  "
-	 * 
- * - * @param str 要填充的字符串,可以为null - * @param size 填充到size长度 - * @param padStr 用来填充的字符串 - * @return right padded String or original String if no padding is necessary, - * 右侧填充字符串 or 如果不需要填充则返回原始字符串 - * {@code null} if null String input - * @since 5.6.2 - */ - public static String rightPad(final String str, final int size, String padStr) { - if (str == null) { - return null; - } - if (isEmpty(padStr)) { - padStr = SPACE; - } - final int padLen = padStr.length(); - final int strLen = str.length(); - final int pads = size - strLen; - if (pads <= 0) { - return str; // 当有可能返回原始字符串; returns original String when possible - } - if (padLen == 1 && pads <= PAD_LIMIT) { - return rightPad(str, size, padStr.charAt(0)); - } - - if (pads == padLen) { - return str.concat(padStr); - } else if (pads < padLen) { - return str.concat(padStr.substring(0, pads)); - } else { - final char[] padding = new char[pads]; - final char[] padChars = padStr.toCharArray(); - for (int i = 0; i < pads; i++) { - padding[i] = padChars[i % padLen]; - } - return str.concat(new String(padding)); - } - } - - /** - *

Gets {@code len} characters from the middle of a String.

- *

从字符串中间获取{@code len}个字符串.

- * - *

If {@code len} characters are not available, the remainder - * of the String will be returned without an exception. If the - * String is {@code null}, {@code null} will be returned. - * An empty String is returned if len is negative or exceeds the - * length of {@code str}.

- * - *

如果{@code len}个字符不可用,则将字符串的其余部分无例外地返回。 - * 如果字符串为{@code null},则将返回{@code null}。 - * 如果len为负或超过{@code str}的长度,则返回一个空字符串。

- * - *

-	 * StrUtil.mid(null, *, *)    = null
-	 * StrUtil.mid(*, *, -ve)     = ""
-	 * StrUtil.mid("", 0, *)      = ""
-	 * StrUtil.mid("abc", 0, 2)   = "ab"
-	 * StrUtil.mid("abc", 0, 4)   = "abc"
-	 * StrUtil.mid("abc", 2, 4)   = "c"
-	 * StrUtil.mid("abc", 4, 2)   = ""
-	 * StrUtil.mid("abc", -2, 2)  = "ab"
-	 * 
- * - * - * @param str 给定字符串,可以为null; the String to get the characters from, may be null - * @param pos 其实位置,如果是负数则当做0; the position to start from, negative treated as zero - * @param len 要求字符串的长度; the length of the required String - * @return the middle characters, {@code null} if null String input - * @since 5.6.2 - */ - public static String mid(final String str, int pos, final int len) { - if (str == null) { - return null; - } - if (len < 0 || pos > str.length()) { - return EMPTY; - } - if (pos < 0) { - pos = 0; - } - if (str.length() <= pos + len) { - return str.substring(pos); - } - return str.substring(pos, pos + len); - } - // Left/Right/Mid 用来兼容 org.apache.commons.lang3.StringUtils 使用习惯 end - //------------------------------------------------------------------------- } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtil.java new file mode 100644 index 000000000..b19b7a941 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtil.java @@ -0,0 +1,217 @@ +package cn.hutool.core.util; + +/** + * 脱敏工具类,支持以下类型信息的脱敏自动处理: + * + * + * + * @author dazer and neusoft and qiaomu + * @since 5.6.2 + */ +public class DesensitizedUtil { + + /** + * 支持的脱敏类型枚举 + * + * @author dazer and neusoft and qiaomu + */ + public enum DesensitizedType { + //用户id + USER_ID, + //中文名 + CHINESE_NAME, + //身份证号 + ID_CARD, + //座机号 + FIXED_PHONE, + //手机号 + MOBILE_PHONE, + //地址 + ADDRESS, + //电子邮件 + EMAIL, + //密码 + PASSWORD + } + + /** + * 脱敏,使用默认的脱敏策略 + *
+	 * DesensitizedUtil.desensitized("100", DesensitizedUtils.DesensitizedType.USER_ID)) =  "0"
+	 * DesensitizedUtil.desensitized("段正淳", DesensitizedUtils.DesensitizedType.CHINESE_NAME)) = "段**"
+	 * DesensitizedUtil.desensitized("51343620000320711X", DesensitizedUtils.DesensitizedType.ID_CARD)) = "5***************1X"
+	 * DesensitizedUtil.desensitized("09157518479", DesensitizedUtils.DesensitizedType.FIXED_PHONE)) = "0915*****79"
+	 * DesensitizedUtil.desensitized("18049531999", DesensitizedUtils.DesensitizedType.MOBILE_PHONE)) = "180****1999"
+	 * DesensitizedUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtils.DesensitizedType.ADDRESS)) = "北京市海淀区马********"
+	 * DesensitizedUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtils.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn"
+	 * DesensitizedUtil.desensitized("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)) = "**********"
+	 * 
+ * + * @author dazer and neusoft and qiaomu + * @param str 字符串 + * @param desensitizedType 脱敏类型;可以脱敏:用户id、中文名、身份证号、座机号、手机号、地址、电子邮件、密码 + * @return 脱敏之后的字符串 + * @since 5.6.2 + */ + public static String desensitized(CharSequence str, DesensitizedUtil.DesensitizedType desensitizedType) { + if (StrUtil.isBlank(str)) { + return StrUtil.EMPTY; + } + String newStr = String.valueOf(str); + switch (desensitizedType) { + case USER_ID: + newStr = String.valueOf(DesensitizedUtil.userId()); + break; + case CHINESE_NAME: + newStr = DesensitizedUtil.chineseName(String.valueOf(str)); + break; + case ID_CARD: + newStr = DesensitizedUtil.idCardNum(String.valueOf(str),1,2); + break; + case FIXED_PHONE: + newStr = DesensitizedUtil.fixedPhone(String.valueOf(str)); + break; + case MOBILE_PHONE: + newStr = DesensitizedUtil.mobilePhone(String.valueOf(str)); + break; + case ADDRESS: + newStr = DesensitizedUtil.address(String.valueOf(str), 8); + break; + case EMAIL: + newStr = DesensitizedUtil.email(String.valueOf(str)); + break; + case PASSWORD: + newStr = DesensitizedUtil.password(String.valueOf(str)); + break; + default: + } + return newStr; + } + + /** + * 【用户id】不对外提供userId + * + * @return 脱敏后的主键 + */ + public static Long userId() { + return 0L; + } + + /** + * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李** + * + * @param fullName 姓名 + * @return 脱敏后的姓名 + */ + public static String chineseName(String fullName) { + if (StrUtil.isBlank(fullName)) { + return StrUtil.EMPTY; + } + final String name = StrUtil.subPre(fullName, 1); + return StrUtil.padAfter(name, StrUtil.length(fullName), "*"); + } + + /** + * 【身份证号】前1位 和后2位 + * + * @param idCardNum 身份证 + * @param front 保留:前面的front位数;从1开始 + * @param end 保留:后面的end位数;从1开始 + * @return 脱敏后的身份证 + */ + public static String idCardNum(String idCardNum, int front, int end) { + //身份证不能为空 + if (StrUtil.isBlank(idCardNum)) { + return StrUtil.EMPTY; + } + //需要截取的长度不能大于身份证号长度 + if ((front + end) > idCardNum.length()) { + return StrUtil.EMPTY; + } + //需要截取的不能小于0 + if (front < 0 || end < 0) { + return StrUtil.EMPTY; + } + return StrUtil.hide(idCardNum, front, idCardNum.length() - end); + } + + /** + * 【固定电话 前四位,后两位 + * + * @param num 固定电话 + * @return 脱敏后的固定电话; + */ + public static String fixedPhone(String num) { + if (StrUtil.isBlank(num)) { + return StrUtil.EMPTY; + } + return StrUtil.hide(num, 4, num.length()-2); + } + + /** + * 【手机号码】前三位,后4位,其他隐藏,比如135****2210 + * + * @param num 移动电话; + * @return 脱敏后的移动电话; + */ + public static String mobilePhone(String num) { + if (StrUtil.isBlank(num)) { + return StrUtil.EMPTY; + } + return StrUtil.hide(num, 3, num.length()-4); + } + + /** + * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区**** + * + * @param address 家庭住址 + * @param sensitiveSize 敏感信息长度 + * @return 脱敏后的家庭地址 + */ + public static String address(String address, int sensitiveSize) { + if (StrUtil.isBlank(address)) { + return StrUtil.EMPTY; + } + int length = address.length(); + return StrUtil.hide(address, length-sensitiveSize, length); + } + + /** + * 【电子邮箱】邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com> + * + * @param email 邮箱 + * @return 脱敏后的邮箱 + */ + public static String email(String email) { + if (StrUtil.isBlank(email)) { + return StrUtil.EMPTY; + } + int index = StrUtil.indexOf(email, '@'); + if (index <= 1) { + return email; + } + return StrUtil.hide(email, 1, index); + } + + /** + * 【密码】密码的全部字符都用*代替,比如:****** + * + * @param password 密码 + * @return 脱敏后的邮箱 + */ + public static String password(String password) { + if (StrUtil.isBlank(password)) { + return StrUtil.EMPTY; + } + return StrUtil.repeat('*', password.length()); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtils.java b/hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtils.java deleted file mode 100644 index a6f64cf31..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/util/DesensitizedUtils.java +++ /dev/null @@ -1,154 +0,0 @@ -package cn.hutool.core.util; - -/** - * 脱敏utils - * @author dazer and neusoft and qiaomu - * @date 2021/3/20 09:55 - * @since 5.6.2 - */ -public class DesensitizedUtils { - public enum DesensitizedType { - //用户id - USER_ID, - //中文名 - CHINESE_NAME, - //身份证号 - ID_CARD, - //座机号 - FIXED_PHONE, - //手机号 - MOBILE_PHONE, - //地址 - ADDRESS, - //电子邮件 - EMAIL, - //密码 - PASSWORD; - } - - /** - * 【用户id】不对外提供userId - * @return 脱敏后的主键 - */ - public static Long userId(){ - return 0L; - } - - /** - * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李** - * - * @param fullName 姓名 - * @return 脱敏后的姓名 - */ - public static String chineseName(String fullName) { - if (StrUtil.isBlank(fullName)) { - return ""; - } - String name = StrUtil.left(fullName, 1); - return StrUtil.rightPad(name, StrUtil.length(fullName), "*"); - } - - /** - * 【身份证号】前1位 和后2位 - * - * @param idCardNum 身份证 - * @param front 保留:前面的front位;从:1开始; - * @param end 保留:后面的end位;从1:开始; - * @return 脱敏后的身份证 - */ - public static String idCardNum(String idCardNum, int front, int end) { - //身份证不能为空 - if (StrUtil.isEmpty(idCardNum)) { - return ""; - } - //需要截取的长度不能大于身份证号长度 - if ((front + end) > idCardNum.length()) { - return ""; - } - //需要截取的不能小于0 - if (front < 0 || end < 0) { - return ""; - } - //计算*的数量 - int asteriskCount = idCardNum.length() - (front + end); - StringBuffer asteriskStr = new StringBuffer(); - for (int i = 0; i < asteriskCount; i++) { - asteriskStr.append("*"); - } - String regex = "(\\w{" + String.valueOf(front) + "})(\\w+)(\\w{" + String.valueOf(end) + "})"; - return idCardNum.replaceAll(regex, "$1" + asteriskStr + "$3"); - } - - /** - * 【固定电话 前四位,后两位 - * - * @param num 固定电话 - * @return 脱敏后的固定电话; - */ - public static String fixedPhone(String num) { - if (StrUtil.isBlank(num)) { - return ""; - } - return StrUtil.left(num, 4).concat(StrUtil.removePrefix(StrUtil.leftPad(StrUtil.right(num, 2), StrUtil.length(num), "*"), "****")); - } - - /** - * 【手机号码】前三位,后4位,其他隐藏,比如135****2210 - * - * @param num 移动电话; - * @return 脱敏后的移动电话; - */ - public static String mobilePhone(String num) { - if (StrUtil.isBlank(num)) { - return ""; - } - return StrUtil.left(num, 3).concat(StrUtil.removePrefix(StrUtil.leftPad(StrUtil.right(num, 4), StrUtil.length(num), "*"), "***")); - } - - /** - * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区**** - * - * @param address 家庭住址 - * @param sensitiveSize 敏感信息长度 - * @return 脱敏后的家庭地址 - */ - public static String address(String address, int sensitiveSize) { - if (StrUtil.isBlank(address)) { - return ""; - } - int length = StrUtil.length(address); - return StrUtil.rightPad(StrUtil.left(address, length - sensitiveSize), length, "*"); - } - - /** - * 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com> - * - * @param email 邮箱 - * @return 脱敏后的邮箱 - */ - public static String email(String email) { - if (StrUtil.isBlank(email)) { - return ""; - } - int index = StrUtil.indexOf(email, '@'); - if (index <= 1) { - return email; - } else { - return StrUtil.rightPad(StrUtil.left(email, 1), index, "*").concat(StrUtil.mid(email, index, StrUtil.length(email))); - } - } - - /** - * 【密码】密码的全部字符都用*代替,比如:****** - * - * @param password 密码 - * @return 脱敏后的邮箱 - */ - public static String password(String password) { - if (StrUtil.isBlank(password)) { - return ""; - } - String pwd = StrUtil.left(password, 0); - return StrUtil.rightPad(pwd, StrUtil.length(password), "*"); - } -} diff --git a/hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilTest.java new file mode 100644 index 000000000..ea2754336 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilTest.java @@ -0,0 +1,79 @@ +package cn.hutool.core.util; + +import org.junit.Assert; +import org.junit.Test; + +/** + * 脱敏工具类 DesensitizedUtils 安全测试 + * + * @author dazer and nuesoft + * @see DesensitizedUtil + */ +public class DesensitizedUtilTest { + + @Test + public void desensitizedTest() { + Assert.assertEquals("0", DesensitizedUtil.desensitized("100", DesensitizedUtil.DesensitizedType.USER_ID)); + Assert.assertEquals("段**", DesensitizedUtil.desensitized("段正淳", DesensitizedUtil.DesensitizedType.CHINESE_NAME)); + Assert.assertEquals("5***************1X", DesensitizedUtil.desensitized("51343620000320711X", DesensitizedUtil.DesensitizedType.ID_CARD)); + Assert.assertEquals("0915*****79", DesensitizedUtil.desensitized("09157518479", DesensitizedUtil.DesensitizedType.FIXED_PHONE)); + Assert.assertEquals("180****1999", DesensitizedUtil.desensitized("18049531999", DesensitizedUtil.DesensitizedType.MOBILE_PHONE)); + Assert.assertEquals("北京市海淀区马********", DesensitizedUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtil.DesensitizedType.ADDRESS)); + Assert.assertEquals("d*************@gmail.com.cn", DesensitizedUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtil.DesensitizedType.EMAIL)); + Assert.assertEquals("**********", DesensitizedUtil.desensitized("1234567890", DesensitizedUtil.DesensitizedType.PASSWORD)); + + Assert.assertEquals("0", DesensitizedUtil.desensitized("100", DesensitizedUtil.DesensitizedType.USER_ID)); + Assert.assertEquals("段**", DesensitizedUtil.desensitized("段正淳", DesensitizedUtil.DesensitizedType.CHINESE_NAME)); + Assert.assertEquals("5***************1X", DesensitizedUtil.desensitized("51343620000320711X", DesensitizedUtil.DesensitizedType.ID_CARD)); + Assert.assertEquals("0915*****79", DesensitizedUtil.desensitized("09157518479", DesensitizedUtil.DesensitizedType.FIXED_PHONE)); + Assert.assertEquals("180****1999", DesensitizedUtil.desensitized("18049531999", DesensitizedUtil.DesensitizedType.MOBILE_PHONE)); + Assert.assertEquals("北京市海淀区马********", DesensitizedUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtil.DesensitizedType.ADDRESS)); + Assert.assertEquals("d*************@gmail.com.cn", DesensitizedUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtil.DesensitizedType.EMAIL)); + Assert.assertEquals("**********", DesensitizedUtil.desensitized("1234567890", DesensitizedUtil.DesensitizedType.PASSWORD)); + } + + @Test + public void userIdTest() { + Assert.assertEquals(Long.valueOf(0L), DesensitizedUtil.userId()); + } + + @Test + public void chineseNameTest() { + Assert.assertEquals("段**", DesensitizedUtil.chineseName("段正淳")); + } + + @Test + public void idCardNumTest() { + Assert.assertEquals("5***************1X", DesensitizedUtil.idCardNum("51343620000320711X", 1, 2)); + } + + @Test + public void fixedPhoneTest() { + Assert.assertEquals("0915*****79", DesensitizedUtil.fixedPhone("09157518479")); + } + + @Test + public void mobilePhoneTest() { + Assert.assertEquals("180****1999", DesensitizedUtil.mobilePhone("18049531999")); + } + + @Test + public void addressTest() { + Assert.assertEquals("北京市海淀区马连洼街*****", DesensitizedUtil.address("北京市海淀区马连洼街道289号", 5)); + Assert.assertEquals("***************", DesensitizedUtil.address("北京市海淀区马连洼街道289号", 50)); + Assert.assertEquals("北京市海淀区马连洼街道289号", DesensitizedUtil.address("北京市海淀区马连洼街道289号", 0)); + Assert.assertEquals("北京市海淀区马连洼街道289号", DesensitizedUtil.address("北京市海淀区马连洼街道289号", -1)); + } + + @Test + public void emailTest() { + Assert.assertEquals("d********@126.com", DesensitizedUtil.email("duandazhi@126.com")); + Assert.assertEquals("d********@gmail.com.cn", DesensitizedUtil.email("duandazhi@gmail.com.cn")); + Assert.assertEquals("d*************@gmail.com.cn", DesensitizedUtil.email("duandazhi-jack@gmail.com.cn")); + } + + @Test + public void passwordTest() { + Assert.assertEquals("**********", DesensitizedUtil.password("1234567890")); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilsTest.java b/hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilsTest.java deleted file mode 100644 index cd5853dc2..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/util/DesensitizedUtilsTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.hutool.core.util; - -import org.junit.Assert; -import org.junit.Test; - -/** - * 脱敏工具类 DesensitizedUtils 安全测试 - * @author dazer and nuesoft - * @date 2021/3/20 22:34 - * @see DesensitizedUtils - */ -public class DesensitizedUtilsTest { - - @Test - public void userIdTest() { - Assert.assertEquals(Long.valueOf(0L), DesensitizedUtils.userId()); - } - - @Test - public void chineseNameTest() { - Assert.assertEquals("段**", DesensitizedUtils.chineseName("段正淳")); - } - - @Test - public void idCardNumTest() { - Assert.assertEquals("5***************1X", DesensitizedUtils.idCardNum("51343620000320711X", 1, 2)); - } - - @Test - public void fixedPhoneTest() { - Assert.assertEquals("0915*****79", DesensitizedUtils.fixedPhone("09157518479")); - } - - @Test - public void mobilePhoneTest() { - Assert.assertEquals("180****1999", DesensitizedUtils.mobilePhone("18049531999")); - } - - @Test - public void addressTest() { - Assert.assertEquals("北京市海淀区马连洼街*****", DesensitizedUtils.address("北京市海淀区马连洼街道289号", 5)); - Assert.assertEquals("***************", DesensitizedUtils.address("北京市海淀区马连洼街道289号", 50)); - Assert.assertEquals("北京市海淀区马连洼街道289号", DesensitizedUtils.address("北京市海淀区马连洼街道289号", 0)); - Assert.assertEquals("北京市海淀区马连洼街道289号", DesensitizedUtils.address("北京市海淀区马连洼街道289号", -1)); - } - - @Test - public void emailTest() { - Assert.assertEquals("d********@126.com", DesensitizedUtils.email("duandazhi@126.com")); - Assert.assertEquals("d********@gmail.com.cn", DesensitizedUtils.email("duandazhi@gmail.com.cn")); - Assert.assertEquals("d*************@gmail.com.cn", DesensitizedUtils.email("duandazhi-jack@gmail.com.cn")); - } - - @Test - public void passwordTest() { - Assert.assertEquals("**********", DesensitizedUtils.password("1234567890")); - } -} diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java index 7a1e18cfb..2c028b794 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java @@ -407,7 +407,7 @@ public class StrUtilTest { Assert.assertNull(StrUtil.padAfter(null, 10, ' ')); Assert.assertEquals("100", StrUtil.padAfter("1", 3, '0')); Assert.assertEquals("23", StrUtil.padAfter("123", 2, '0')); - Assert.assertEquals("23", StrUtil.padAfter("123", -1, '0')); + Assert.assertEquals("", StrUtil.padAfter("123", -1, '0')); Assert.assertNull(StrUtil.padAfter(null, 10, "ABC")); Assert.assertEquals("1AB", StrUtil.padAfter("1", 3, "ABC")); @@ -498,81 +498,6 @@ public class StrUtilTest { Assert.assertEquals("this is a for 1,000", ret); } - @Test - public void leftTest() { - Assert.assertNull(StrUtil.left(null, 1)); - Assert.assertEquals("", StrUtil.left("neu", -1)); - Assert.assertEquals("", StrUtil.left("", 1)); - Assert.assertEquals("", StrUtil.left("neu", 0)); - Assert.assertEquals("ne", StrUtil.left("neu", 2)); - Assert.assertEquals("neu", StrUtil.left("neu", 4)); - } - - @Test - public void leftPadTest() { - Assert.assertNull(StrUtil.leftPad(null, 1)); - Assert.assertEquals("", StrUtil.leftPad("", -1)); - Assert.assertEquals(" ", StrUtil.leftPad("", 3)); - Assert.assertEquals("bat", StrUtil.leftPad("bat", -1)); - Assert.assertEquals("bat", StrUtil.leftPad("bat", 1)); - Assert.assertEquals("bat", StrUtil.leftPad("bat", 3)); - Assert.assertEquals(" bat", StrUtil.leftPad("bat", 5)); - - Assert.assertNull(StrUtil.leftPad(null, 1, "z")); - Assert.assertEquals("", StrUtil.leftPad("", -1, "z")); - Assert.assertEquals("zzz", StrUtil.leftPad("", 3, "z")); - Assert.assertEquals("bat", StrUtil.leftPad("bat", -1, "z")); - Assert.assertEquals("bat", StrUtil.leftPad("bat", 1, "z")); - Assert.assertEquals("bat", StrUtil.leftPad("bat", 3, "z")); - Assert.assertEquals("zzbat", StrUtil.leftPad("bat", 5, "z")); - } - - @Test - public void rightTest() { - Assert.assertNull(StrUtil.right(null, 1)); - Assert.assertEquals("", StrUtil.right("neu", -1)); - Assert.assertEquals("", StrUtil.right("", 1)); - Assert.assertEquals("", StrUtil.right("neu", 0)); - Assert.assertEquals("eu", StrUtil.right("neu", 2)); - Assert.assertEquals("neu", StrUtil.right("neu", 4)); - } - - @Test - public void rightPadTest() { - Assert.assertNull(StrUtil.rightPad(null, 1)); - Assert.assertEquals(" ", StrUtil.rightPad("", 3)); - Assert.assertEquals("bat", StrUtil.rightPad("bat", 3)); - Assert.assertEquals("bat ", StrUtil.rightPad("bat", 5)); - Assert.assertEquals("bat", StrUtil.rightPad("bat", 1)); - Assert.assertEquals("bat", StrUtil.rightPad("bat", -1)); - - Assert.assertNull(StrUtil.rightPad(null, 1, 'z')); - Assert.assertEquals("zzz", StrUtil.rightPad("", 3, 'z')); - Assert.assertEquals("bat", StrUtil.rightPad("bat", 3, 'z')); - Assert.assertEquals("batzz", StrUtil.rightPad("bat", 5, 'z')); - Assert.assertEquals("bat", StrUtil.rightPad("bat", 1, 'z')); - Assert.assertEquals("bat", StrUtil.rightPad("bat", -1, 'z')); - - Assert.assertNull(StrUtil.rightPad(null, 1, "z")); - Assert.assertEquals("zzz", StrUtil.rightPad("", 3, "z")); - Assert.assertEquals("bat", StrUtil.rightPad("bat", 3, "z")); - Assert.assertEquals("batzz", StrUtil.rightPad("bat", 5, "z")); - Assert.assertEquals("bat", StrUtil.rightPad("bat", 1, "z")); - Assert.assertEquals("bat", StrUtil.rightPad("bat", -1, "z")); - } - - @Test - public void midTest() { - Assert.assertNull(StrUtil.mid(null, 1, 1)); - Assert.assertEquals("", StrUtil.mid("abc", 1, -1)); - Assert.assertEquals("", StrUtil.mid("", 0, 1)); - Assert.assertEquals("ab", StrUtil.mid("abc", 0, 2)); - Assert.assertEquals("abc", StrUtil.mid("abc", 0, 4)); - Assert.assertEquals("c", StrUtil.mid("abc", 2, 4)); - Assert.assertEquals("", StrUtil.mid("abc", 4, 2)); - Assert.assertEquals("ab", StrUtil.mid("abc", -2, 2)); - } - @Test public void hideTest() { Assert.assertNull(StrUtil.hide(null, 1, 1)); @@ -582,24 +507,5 @@ public class StrUtilTest { Assert.assertEquals("jackduan@163.com", StrUtil.hide("jackduan@163.com", 3, 2)); Assert.assertEquals("jackduan@163.com", StrUtil.hide("jackduan@163.com", 16, 16)); Assert.assertEquals("jackduan@163.com", StrUtil.hide("jackduan@163.com", 16, 17)); - - - Assert.assertEquals("0", StrUtil.hide("100", DesensitizedUtils.DesensitizedType.USER_ID)); - Assert.assertEquals("段**", StrUtil.hide("段正淳", DesensitizedUtils.DesensitizedType.CHINESE_NAME)); - Assert.assertEquals("5***************1X", StrUtil.hide("51343620000320711X", DesensitizedUtils.DesensitizedType.ID_CARD)); - Assert.assertEquals("0915*****79", StrUtil.hide("09157518479", DesensitizedUtils.DesensitizedType.FIXED_PHONE)); - Assert.assertEquals("180****1999", StrUtil.hide("18049531999", DesensitizedUtils.DesensitizedType.MOBILE_PHONE)); - Assert.assertEquals("北京市海淀区马********", StrUtil.hide("北京市海淀区马连洼街道289号", DesensitizedUtils.DesensitizedType.ADDRESS)); - Assert.assertEquals("d*************@gmail.com.cn", StrUtil.hide("duandazhi-jack@gmail.com.cn", DesensitizedUtils.DesensitizedType.EMAIL)); - Assert.assertEquals("**********", StrUtil.hide("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)); - - Assert.assertEquals("0", StrUtil.desensitized("100", DesensitizedUtils.DesensitizedType.USER_ID)); - Assert.assertEquals("段**", StrUtil.desensitized("段正淳", DesensitizedUtils.DesensitizedType.CHINESE_NAME)); - Assert.assertEquals("5***************1X", StrUtil.desensitized("51343620000320711X", DesensitizedUtils.DesensitizedType.ID_CARD)); - Assert.assertEquals("0915*****79", StrUtil.desensitized("09157518479", DesensitizedUtils.DesensitizedType.FIXED_PHONE)); - Assert.assertEquals("180****1999", StrUtil.desensitized("18049531999", DesensitizedUtils.DesensitizedType.MOBILE_PHONE)); - Assert.assertEquals("北京市海淀区马********", StrUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtils.DesensitizedType.ADDRESS)); - Assert.assertEquals("d*************@gmail.com.cn", StrUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtils.DesensitizedType.EMAIL)); - Assert.assertEquals("**********", StrUtil.desensitized("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)); } }