|
@@ -0,0 +1,145 @@
|
|
|
+package org.jeecg.common.util.idCard;
|
|
|
+
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.format.DateTimeParseException;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 18位身份证工具类(生产环境专用)
|
|
|
+ */
|
|
|
+public class ChineseIdCard18Utils {
|
|
|
+
|
|
|
+ // 身份证校验码系数
|
|
|
+ private static final int[] WEIGHT_COEFFICIENT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
|
|
|
+ // 校验码对应值
|
|
|
+ private static final char[] CHECK_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
|
|
|
+ // 18位身份证正则
|
|
|
+ private static final Pattern ID_18_REGEX = Pattern.compile(
|
|
|
+ "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9X]$"
|
|
|
+ );
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从身份证号提取出生日期
|
|
|
+ *
|
|
|
+ * @param idCard 18位身份证号码
|
|
|
+ * @return 出生日期(yyyy - MM - dd格式)
|
|
|
+ * @throws IllegalArgumentException 身份证号无效时抛出
|
|
|
+ */
|
|
|
+ public static String extractBirthDate(String idCard) {
|
|
|
+ validateIdCard(idCard);
|
|
|
+ String birthDateStr = idCard.substring(6, 14);
|
|
|
+
|
|
|
+ try {
|
|
|
+ DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
|
|
+ DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ return LocalDate.parse(birthDateStr, inputFormatter)
|
|
|
+ .format(outputFormatter);
|
|
|
+ } catch (DateTimeParseException e) {
|
|
|
+ throw new IllegalArgumentException("无效的出生日期编码", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从身份证号提取性别
|
|
|
+ *
|
|
|
+ * @param idCard 18位身份证号码
|
|
|
+ * @return 性别枚举
|
|
|
+ * @throws IllegalArgumentException 身份证号无效时抛出
|
|
|
+ */
|
|
|
+ public static Gender extractGender(String idCard) {
|
|
|
+ validateIdCard(idCard);
|
|
|
+ char genderChar = idCard.charAt(16);
|
|
|
+ return (genderChar % 2 == 1) ? Gender.MALE : Gender.FEMALE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 身份证脱敏
|
|
|
+ *
|
|
|
+ * @param idCard 18位身份证号码
|
|
|
+ * @param prefixKeepLen 保留前几位
|
|
|
+ * @param suffixKeepLen 保留后几位
|
|
|
+ * @return 脱敏后的身份证号
|
|
|
+ */
|
|
|
+ public static String getDesensitization(String idCard,int prefixKeepLen, int suffixKeepLen) {
|
|
|
+ validateIdCard(idCard);
|
|
|
+ return mask(idCard,prefixKeepLen,suffixKeepLen);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自定义脱敏方法
|
|
|
+ * @param idCard 身份证号码
|
|
|
+ * @param prefixKeepLen 保留前几位
|
|
|
+ * @param suffixKeepLen 保留后几位
|
|
|
+ * @return 脱敏后的身份证号
|
|
|
+ */
|
|
|
+ private static String mask(String idCard, int prefixKeepLen, int suffixKeepLen) {
|
|
|
+
|
|
|
+ // 计算需要脱敏的部分
|
|
|
+ int totalMaskLen = 18 - prefixKeepLen - suffixKeepLen;
|
|
|
+ if (totalMaskLen <= 0) {
|
|
|
+ return idCard; // 无需脱敏
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建脱敏字符串
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append(idCard.substring(0, prefixKeepLen));
|
|
|
+ sb.append("*".repeat(totalMaskLen));
|
|
|
+ sb.append(idCard.substring(18 - suffixKeepLen));
|
|
|
+
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证18位身份证有效性
|
|
|
+ *
|
|
|
+ * @throws IllegalArgumentException 身份证号无效时抛出
|
|
|
+ */
|
|
|
+ public static void validateIdCard(String idCard) {
|
|
|
+ Objects.requireNonNull(idCard, "身份证号不能为空");
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(idCard)) {
|
|
|
+ throw new IllegalArgumentException("身份证号不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ idCard = idCard.trim().toUpperCase();
|
|
|
+
|
|
|
+ if (!ID_18_REGEX.matcher(idCard).matches()) {
|
|
|
+ throw new IllegalArgumentException("身份证号格式不正确");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!validateCheckDigit(idCard)) {
|
|
|
+ throw new IllegalArgumentException("身份证校验码无效");
|
|
|
+ }
|
|
|
+
|
|
|
+ validateBirthDate(idCard);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验码验证
|
|
|
+ */
|
|
|
+ private static boolean validateCheckDigit(String idCard) {
|
|
|
+ int sum = 0;
|
|
|
+ for (int i = 0; i < 17; i++) {
|
|
|
+ sum += (idCard.charAt(i) - '0') * WEIGHT_COEFFICIENT[i];
|
|
|
+ }
|
|
|
+ char checkCode = CHECK_CODE[sum % 11];
|
|
|
+ return checkCode == idCard.charAt(17);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 出生日期验证
|
|
|
+ */
|
|
|
+ private static void validateBirthDate(String idCard) {
|
|
|
+ String birthDateStr = idCard.substring(6, 14);
|
|
|
+ try {
|
|
|
+ LocalDate.parse(birthDateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
|
|
|
+ } catch (DateTimeParseException e) {
|
|
|
+ throw new IllegalArgumentException("无效的出生日期编码");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|