身份证号校验规则最全总结(保证干湿分离)

11,847 阅读4分钟

一、背景介绍

前两天测试小姐姐突然找到我,说我的表单校验是不是有问题,为什么一个格式很标准的身份证号校验总是提示校验失败呢?能改一下校验吗,不能然每次都要使用她的真实身份证号来测试了...

好吧,最后给她推荐了一个身份证号码生成网站解决了她的困扰。当然,也顺便给我的同事们分享了一波我使用的校验方法,就像接入了公安系统一样可以校验出那些自己编造的身份证号。

二、校验规则介绍

对身份证号生成规则比较熟悉的小伙伴可能已经猜到了,我使用的是ISO 7064:1983.MOD 11-2校验算法。简单的说就是将18位的身份证号的前17位与对应的加权因子相乘对11取余,然后余数对应的code就应该是身份证号的第18位,获取到的值与第十八位相比如果相等,那就校验通过。

听起来是不是很简单,只要我们任意输入17位数字生成第18位,这个18位数字就能通过以上校验,接下来详细介绍一下校验流程~

1.校验流程

我们以11111111111111111(17个1)这个数字为例,对这个算法一探究竟。小伙伴们也可以随便你身份证号的前17位一起走一遍流程。

(1) 加权因子求和

17 位的加权因子分别是 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2

1*7 + 1*9 + 1*10 + 1*5 + 1*8 1*4 + 1*2 + 1*1 + 1*6 + 1*3 + 1*7 + 1*9 + 1*10 + 1*5 + 1*8 + 1*4 + 1*2 

分别求和得到的结果是100

(2) 对11取余

100 % 11 = 1

(3) 与12差值再对11取余

(12 - 1) % 11 = 0

(4) 验证

验证网站

验证结果如图 idcard.png

2.其他校验

如果身份证号校验只用ISO 7064:1983.MOD 11-2是不够严谨的,有的读者这可能已经想到了,因为任意一段长度为17的数字加上经过计算的校验码,那前17位数字可能可能都是不符合基本规则的,比如错误的日期格式,非法的地址码等等。

这里注意一点,长度为18的数字已经超出了js数值的计算范围,所以校验时可以转化为数组或者字符串。更详细的规则如下:

  • 第一位不可能是0
  • 第二位到第六位可以是0-9
  • 第七位到第十位是年份,所以七八位为19或者20
  • 十一位和十二位是月份,这两位是01-12之间的数值
  • 十三位和十四位是日期,是从01-31之间的数值
  • 十五,十六,十七都是数字0-9
  • 十八位可能是数字0-9,也可能是X

身份证号的的详细规则请看文末的总结。

三、代码

包含上方提到的两种校验。

function checkIDCard (idcode) {
  // 加权因子
  const weightFactor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
  // 校验码
  const checkCode = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']

  const code = String(idcode)
  const last = idcode[17]// 最后一位

  const seventeen = code.substring(0, 17)

  // ISO 7064:1983.MOD 11-2 判断最后一位校验码是否正确
  const arr = seventeen.split('')
  const len = arr.length
  let num = 0
  for (let i = 0; i < len; i++) {
    num = num + arr[i] * weightFactor[i]
  }

  // 获取余数
  const resisue = num % 11
  const lastNo = checkCode[resisue]

  // 身份证号格式的正则思路
  const idcardPatter = /^[1-9][0-9]{5}([1][9][0-9]{2}|[2][0][0|1][0-9])([0][1-9]|[1][0|1|2])([0][1-9]|[1|2][0-9]|[3][0|1])[0-9]{3}([0-9]|[X])$/

  // 判断格式是否正确
  const format = idcardPatter.test(idcode)

  // 返回验证结果,校验码和格式同时正确才算是合法的身份证号码
  return last === lastNo && format
}
  • 代码中使用了一个数组直接转化最后的结果实际与二、1.(3)操作的结果是一样的
  • 使用该方法传入的参数需要是一个字符串

四、校验优化

注意:以下函数的入参都必须是一个字符串。

1.通过身份证号码获取性别

const cardToSex = idNo => {
  idNo = idNo.replace(/\s*/g, '')
  let sexStr = ''
  if (idNo.length === 15) {
    sexStr = parseInt(idNo.substring(14, 1), 10) % 2 ? '男' : '女'
  } else if (idNo.length === 18) {
    sexStr = parseInt(idNo.substring(17, 1), 10) % 2 ? '男' : '女'
  }
  return sexStr
}

2.通过身份证号码获取出生年月日

const cardToBirthday = (idNo, split = '-') => {
  idNo = idNo.replace(/\s*/g, '')
  let tmpStr = ''
  if (idNo.length === 15) {
    tmpStr = idNo.substring(6, 12)
    tmpStr = '19' + tmpStr
    tmpStr = tmpStr.substring(0, 4) + split + tmpStr.substring(4, 6) + split + tmpStr.substring(6)
  } else if (idNo.length === 18) {
    tmpStr = idNo.substring(6, 14)
    tmpStr = tmpStr.substring(0, 4) + split + tmpStr.substring(4, 6) + split + tmpStr.substring(6)
  }
  return tmpStr
}

五、身份证号编码规则

位数含义
1~2所在省(直辖市、自治区)的代码
3~4所在地级市(自治州)的代码
5~6所在区(县、自治县、县级市)的代码
7~10出生年份
11~12出生月份
13~14出生日
15~16所在派出所代码
17奇数代表男性,偶数代表女性
18校验码,生成规则参考上方的ISO 7064:1983.MOD 11-2算法

前六位详细地址代码可以参考这里

哈哈,内容逐渐由湿转干~觉得有用别忘了点个赞,建议收藏

END