Created
January 20, 2017 14:08
-
-
Save caoxudong/83e4d65ec0faa4eabba81c3ae8dfebdb to your computer and use it in GitHub Desktop.
校验身份证格式
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package demo.tool; | |
import java.text.ParseException; | |
import java.text.SimpleDateFormat; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import demo.constants.Gender; | |
import demo.exception.InvalidParameterException; | |
import demo.entity.IDCertNoValidateResult; | |
public class IDCertNoTool { | |
private static final Logger LOGGER = LoggerFactory.getLogger(IDCertNoTool.class); | |
public static final String INVALID_FIELD_NAME_CERT_NO = "certNo"; | |
public static final String INVALID_FIELD_NAME_AREA_CODE = "areaCode"; | |
public static final String INVALID_FIELD_NAME_BIRTHDAY = "birthday"; | |
public static final String INVALID_FIELD_NAME_CHECK_CHAR = "checkChar"; | |
public static final String INVALID_FIELD_NAME_GENDER = "gender"; | |
private final static char[] PARITYBIT = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; | |
private final static int[] POWER_LIST = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; | |
private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmssSSS") { | |
private static final long serialVersionUID = 1L; | |
{ | |
this.setLenient(false); | |
} | |
}; | |
private final static String BIRTHDAY_PADDING = "000000000"; | |
private final static Map<String, String> ZONE_NUMBER = new HashMap<String, String>() { | |
private static final long serialVersionUID = 1L; | |
{ | |
this.put("11", "北京"); | |
this.put("12", "天津"); | |
this.put("13", "河北"); | |
this.put("14", "山西"); | |
this.put("15", "内蒙古"); | |
this.put("21", "辽宁"); | |
this.put("22", "吉林"); | |
this.put("23", "黑龙江"); | |
this.put("31", "上海"); | |
this.put("32", "江苏"); | |
this.put("33", "浙江"); | |
this.put("34", "安徽"); | |
this.put("35", "福建"); | |
this.put("36", "江西"); | |
this.put("37", "山东"); | |
this.put("41", "河南"); | |
this.put("42", "湖北"); | |
this.put("43", "湖南"); | |
this.put("44", "广东"); | |
this.put("45", "广西"); | |
this.put("46", "海南"); | |
this.put("50", "重庆"); | |
this.put("51", "四川"); | |
this.put("52", "贵州"); | |
this.put("53", "云南"); | |
this.put("54", "西藏"); | |
this.put("61", "陕西"); | |
this.put("62", "甘肃"); | |
this.put("63", "青海"); | |
this.put("64", "宁夏"); | |
this.put("65", "新疆"); | |
this.put("71", "台湾"); | |
this.put("81", "香港"); | |
this.put("82", "澳门"); | |
this.put("91", "外国"); | |
} | |
}; | |
public static enum IDCertNoType { | |
OLD(15), | |
NEW(18); | |
private int length; | |
private IDCertNoType(int length) { | |
this.length = length; | |
} | |
public int getLength() { | |
return length; | |
} | |
public static IDCertNoType resolve(int length) { | |
for (IDCertNoType role : values()) { | |
if (role.getLength() == length) { | |
return role; | |
} | |
} | |
throw new IllegalArgumentException("unknown cert number type for length: " + length); | |
} | |
} | |
/** | |
* 身份证验证,验证通过,返回性别和出生日期 | |
* | |
* @param certNo 号码内容 | |
* @return 身份证中包含的性别和生日信息 | |
*/ | |
public static IDCertNoValidateResult validate(String certNo) throws InvalidParameterException { | |
char[] chars = certNo.toCharArray(); | |
int certNoLength = chars.length; | |
IDCertNoType certNoType = null; | |
try { | |
certNoType = IDCertNoType.resolve(certNoLength); | |
} catch (Exception e) { | |
String msg = "invalid cert number length: " + certNoLength; | |
LOGGER.error(msg); | |
Map<String, String> errorDesc = new HashMap<>(); | |
errorDesc.put(INVALID_FIELD_NAME_CERT_NO, msg); | |
throw new InvalidParameterException(errorDesc); | |
} | |
switch (certNoType) { | |
case OLD: | |
return validateOldCertNo(chars); | |
case NEW: | |
return validateNewCertNo(chars); | |
default: { | |
String msg = "unsupported IDCertNoType: " + certNoType; | |
LOGGER.error(msg); | |
Map<String, String> errorDesc = new HashMap<>(); | |
errorDesc.put(INVALID_FIELD_NAME_CERT_NO, msg); | |
throw new InvalidParameterException(errorDesc); | |
} | |
} | |
} | |
private static IDCertNoValidateResult validateNewCertNo(char[] chars) throws InvalidParameterException { | |
checkAreaCode(chars, 0, 6); | |
String birthdayStr = new String(chars, 6, 8) + BIRTHDAY_PADDING; | |
Date birthday = parseBirthday(birthdayStr); | |
Gender gender = parseGender(chars[16]); | |
validateCheckChar(chars); | |
return new IDCertNoValidateResult(gender, birthday); | |
} | |
/** | |
* 校验旧版身份证号,这里在处理年龄时,会根据年龄位进行判断,若是"0x"则补充为"200x",否则补充为"19xx"。 | |
* | |
* @param certNo 旧版身份证号 | |
* @return | |
* @throws InvalidParameterException | |
*/ | |
private static IDCertNoValidateResult validateOldCertNo(char[] chars) throws InvalidParameterException { | |
checkAreaCode(chars, 0, 6); | |
StringBuilder birthdayBuilder = new StringBuilder(); | |
char firstBirthdayChar = chars[6]; | |
switch (firstBirthdayChar) { | |
case '0': | |
birthdayBuilder.append("20").append(chars, 6, 6).append(BIRTHDAY_PADDING); | |
break; | |
default: | |
birthdayBuilder.append("19").append(chars, 6, 6).append(BIRTHDAY_PADDING); | |
break; | |
} | |
Date birthday = parseBirthday(birthdayBuilder.toString()); | |
Gender gender = parseGender(chars[14]); | |
return new IDCertNoValidateResult(gender, birthday); | |
} | |
private static void checkAreaCode(char[] chars, int startIndex, int length) | |
throws InvalidParameterException { | |
String areaCode = new String(chars, startIndex, 2); | |
if (!ZONE_NUMBER.containsKey(areaCode)) { | |
String msg = "parse area code failed: " + areaCode; | |
LOGGER.error(msg); | |
Map<String, String> errorDesc = new HashMap<>(); | |
errorDesc.put(INVALID_FIELD_NAME_AREA_CODE, msg); | |
throw new InvalidParameterException(errorDesc); | |
} | |
} | |
private static Date parseBirthday(String birthdayStr) throws InvalidParameterException { | |
Date birthday = null; | |
try { | |
birthday = DATE_FORMAT.parse(birthdayStr); | |
} catch (ParseException e) { | |
String msg = "parse birthday failed: " + birthdayStr; | |
LOGGER.error(msg); | |
Map<String, String> errorDesc = new HashMap<>(); | |
errorDesc.put(INVALID_FIELD_NAME_BIRTHDAY, msg); | |
throw new InvalidParameterException(errorDesc); | |
} | |
return birthday; | |
} | |
private static Gender parseGender(char genderChar) throws InvalidParameterException { | |
if ((genderChar < '0') || (genderChar > '9')) { | |
String msg = "parse gender failed: " + genderChar; | |
LOGGER.error(msg); | |
Map<String, String> errorDesc = new HashMap<>(); | |
errorDesc.put(INVALID_FIELD_NAME_GENDER, msg); | |
throw new InvalidParameterException(errorDesc); | |
} | |
if (((genderChar - '0') & 1) == 0) { | |
return Gender.F; | |
} else { | |
return Gender.M; | |
} | |
} | |
private static void validateCheckChar(char[] chars) throws InvalidParameterException { | |
char computedCheckChar = computeCheckChar(chars, 0, 17); | |
char targetCheckChar = chars[17]; | |
if (computedCheckChar != targetCheckChar) { | |
String msg = "validate check char failed: " + targetCheckChar; | |
LOGGER.error(msg); | |
Map<String, String> errorDesc = new HashMap<>(); | |
errorDesc.put(INVALID_FIELD_NAME_CHECK_CHAR, msg); | |
throw new InvalidParameterException(errorDesc); | |
} | |
} | |
private static char computeCheckChar(char[] chars, int startIndex, int length) { | |
int checkSum = 0; | |
for (int i = 0; i < length; i++) { | |
char c = chars[startIndex + i]; | |
checkSum += (c - '0') * POWER_LIST[i]; | |
} | |
int checkCharIndex = checkSum % 11; | |
char checkChar = PARITYBIT[checkCharIndex]; | |
return checkChar; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment