Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
身份证验证的工具
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* IDCardUtil
* <p>
* 身份证验证的工具(支持15位或18位身份证)
* 身份证号码结构:
* 17位数字和1位校验码:6位地址码数字,8位生日数字,3位出生时间顺序号,1位校验码。
* 地址码(前6位):表示对象常住户口所在县(市、镇、区)的行政区划代码,按GB/T2260的规定执行。
* 出生日期码,(第七位 至十四位):表示编码对象出生年、月、日,按GB按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
* 顺序码(第十五位至十七位):表示在同一地址码所标示的区域范围内,对同年、同月、同日出生的人编订的顺序号,
* 顺序码的奇数分配给男性,偶数分配给女性。
* 校验码(第十八位数):
* 十七位数字本体码加权求和公式 s = sum(Ai*Wi), i = 18…2,先对前17位数字的权求和;
* Ai:表示第i位置上的身份证号码数字值。
* Wi:表示第i位置上的加权因子,采用的是国际标准化组织ISO订立的《ISO 7064: 1983》中的“MOD 11-2”校验码系统。
* 计算方法是 2的 i-1 次方除以11的余数,公式: Wi = mod( 2^(i-1) , 11) ,
* 计算后的结果 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2;
* 计算模, 本体码除以11的余数 Y = mod(s, 11)
* 通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 <=> 校验码: 1 0 X 9 8 7 6 5 4 3 2
*
* @author chingow
* @date 2019/11/27 20:45
*/
public class IDCardUtil {
final static Map<Integer, String> zoneNum = new HashMap<>();
final static int[] POWER_LIST = new int[17];
static Pattern pattern = Pattern.compile("^\\d{17}[\\d|X]$");
static {
zoneNum.put(11, "北京");
zoneNum.put(12, "天津");
zoneNum.put(13, "河北");
zoneNum.put(14, "山西");
zoneNum.put(15, "内蒙古");
zoneNum.put(21, "辽宁");
zoneNum.put(22, "吉林");
zoneNum.put(23, "黑龙江");
zoneNum.put(31, "上海");
zoneNum.put(32, "江苏");
zoneNum.put(33, "浙江");
zoneNum.put(34, "安徽");
zoneNum.put(35, "福建");
zoneNum.put(36, "江西");
zoneNum.put(37, "山东");
zoneNum.put(41, "河南");
zoneNum.put(42, "湖北");
zoneNum.put(43, "湖南");
zoneNum.put(44, "广东");
zoneNum.put(45, "广西");
zoneNum.put(46, "海南");
zoneNum.put(50, "重庆");
zoneNum.put(51, "四川");
zoneNum.put(52, "贵州");
zoneNum.put(53, "云南");
zoneNum.put(54, "西藏");
zoneNum.put(61, "陕西");
zoneNum.put(62, "甘肃");
zoneNum.put(63, "青海");
zoneNum.put(64, "新疆");
zoneNum.put(71, "台湾");
zoneNum.put(81, "香港");
zoneNum.put(82, "澳门");
zoneNum.put(91, "外国");
}
static {
// 初始化加权因子 Wi
for (int i = 0; i < 17; i++) {
// 计算方法是 2的17-i 次方除以11的余数,公式: Wi = mod( 2^(17-i) , 11) ,
POWER_LIST[i] = (int) Math.pow(Double.valueOf(2), Double.valueOf(17 - i)) % 11;
}
}
final static char[] PARITY_BIT = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
/**
* 身份证验证
*
* @param certNo 身份证号码
* @return 是否有效 null和"" 都是false
*/
public static boolean isIDCard(String certNo) {
if (certNo == null || !pattern.matcher(certNo = certNo.toUpperCase()).matches()) {
return false;
}
//校验区位码
if (!zoneNum.containsKey(Integer.valueOf(certNo.substring(0, 2)))) {
return false;
}
//校验出生日期码
String birthday = certNo.length() == 15 ? "19" + certNo.substring(6, 12) : certNo.substring(6, 14);
DateFormat fmt = new SimpleDateFormat("yyyyMMdd");
try {
// 如果日期超过当前时间说明日期错误
if (fmt.parse(birthday).after(Calendar.getInstance().getTime())) {
return false;
}
} catch (Exception e) {
// 如果throw java.text.ParseException或者NullPointerException,就说明格式不对
return false;
}
//校验"校验码" , 15 位不需要校验
if (certNo.length() == 15) {
return true;
}
return certNo.substring(17).equals(String.valueOf(getCheckCode(certNo)));
}
/**
* 获取18位身份证号码
*
* @param certNo 身份证号码
* @return 15位身份证号转化为18位返回,非15位身份证号原值返回
*/
public static String get18Ic(String certNo) {
if (certNo == null || certNo.length() != 15) {
return certNo;
}
// 1999年10月开始实行18位身份证号码,故15位身份证号码都是19XX年的
String ic17 = new StringBuffer()
.append(certNo.substring(0, 6))
.append("19")
.append(certNo.substring(6)).toString();
return ic17 + getCheckCode(ic17);
}
/**
* 获取身份证号码校验码
*
* @param certNo
* @return
*/
private static char getCheckCode(String certNo) {
char[] cs = certNo.toCharArray();
//校验位数
int power = 0;
// 取身份证前17位
for (int i = 0; i < 17; i++) {
power += (cs[i] - '0') * POWER_LIST[i];
}
return PARITY_BIT[power % 11];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.