Skip to content

Instantly share code, notes, and snippets.

@caoxudong
Created January 20, 2017 14:08
Show Gist options
  • Save caoxudong/83e4d65ec0faa4eabba81c3ae8dfebdb to your computer and use it in GitHub Desktop.
Save caoxudong/83e4d65ec0faa4eabba81c3ae8dfebdb to your computer and use it in GitHub Desktop.
校验身份证格式
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