Java验证身份证号码有效性的完整实现与实时校验

Elena
Elena
发布时间:2026-10-15 | 2人浏览过
原创

在Java开发中,身份证号码的有效性校验是数据录入的关键环节。本文将深入解析18位身份证的国家标准算法,并通过完整的工具类演示如何实现长度、区位、日期合法性及校验位的全面验证,同时提供适用于前端输入框的“边输入边校验”策略。

身份证结构与标准规范

中国大陆居民身份证主要分为15位旧版和18位新版。理解其结构是实现精准校验的前提:

  • 地址码(前6位):反映居住地的行政区划代码。例如前两位代表省,中间两位代表地市,后两位代表县区。
  • 生日码(7-14位):表示出生年、月、日。18位证为YYYYMMDD格式(如19900101),15位证为YYMMDD格式(如900101,默认归属19xx年)。
  • 顺序码(15-17位):同一区域内同年同月日出生人员的顺序号。其中第17位奇数表示男性,偶数表示女性。
  • 校验码(第18位):通过ISO 7064:1983.MOD 11-2校验算法得出。若计算结果为10,则用罗马数字X代替,以保证号码长度为18位。

核心校验算法原理

对于18位身份证,校验位的有效性依赖于前17位数字的加权求和运算:

  1. 加权因子:前17位每一位对应一个权重,分别为:{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}。
  2. 模运算:将各位数与权重相乘后累加,结果除以11取余数。
  3. 映射对照:余数与校验位有一一对应关系:{1, 0, X, 9, 8, 7, 6, 5, 4, 3, 2}。例如余数为2,则第18位必须是X。

全功能身份证校验工具类实现

以下代码实现了包含格式检测、区域验证、复杂日期逻辑(闰年/大小月)及校验码计算的完整校验方法。适用于后台批量数据处理或表单提交后的最终审核。

操作步骤:

第一步:定义校验常量和地区字典

初始化省份编码映射表和加权因子数组,确保后续逻辑可配置且高效。

package cn.wje.internationa;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

public class IdCardUtil {
    // ... (省略包名导入,保持精简) ...

    final static Map<Integer, String> zoneNum = new HashMap<>();
    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(65, "新疆"); zoneNum.put(71, "台湾");
        zoneNum.put(81, "香港"); zoneNum.put(82, "澳门");
        zoneNum.put(91, "国外");
    }

    /**
     * 校验码常量映射
     */
    final static int[] PARITYBIT = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
    /**
     * 加权因子wi
     */
    final static int[] POWER_LIST = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
}...

第二步:实现综合校验逻辑

在 isIdcard 方法中,依次执行长度判断、区号存在性检查、日期合法性解析以及校验位比对。特别注意使用 SimpleDateFormat.setLenient(false) 来严格拦截非法日期(如2月30日)。

...
    public static boolean isIdcard(String idCard) {
        // 1. 基础长度检查
        if (idCard == null || (idCard.length() != 15 && idCard.length() != 18)) {
            return false;
        }
        // 2. 校验区位码
        if (!zoneNum.containsKey(Integer.valueOf(idCard.substring(0, 2)))) {
            return false;
        }
        // 3. 提取并校验年月日
        String year = idCard.length() == 15 ? "19" + idCard.substring(6, 8) : idCard.substring(6, 10);
        String month = idCard.length() == 15 ? idCard.substring(8, 10) : idCard.substring(10, 12);
        String day = idCard.length() == 15 ? idCard.substring(10, 12) : idCard.substring(12, 14);
        
        // 简单范围检查
        int iyear = Integer.parseInt(year);
        if (iyear < 1900 || iyear > Calendar.getInstance().get(Calendar.YEAR)) return false;
        int imonth = Integer.parseInt(month);
        if (imonth < 1 || imonth > 12) return false;
        int iday = Integer.parseInt(day);
        if (iday < 1 || iday > 31) return false;
        
        // 4. 严格日期格式校验(处理闰年等)
        if (!isValidDate(year + month + day)) {
            return false;
        }
        
        // 5. 校验位验证(针对18位)
        if (idCard.length() == 15) return true;
        
        int power = 0;
        char[] cs = idCard.toUpperCase().toCharArray();
        for (int i = 0; i < cs.length; i++) {
            if (i == cs.length - 1 && cs[i] == 'X') break;
            if (cs[i] < '0' || cs[i] > '9') return false;
            if (i < cs.length - 1) {
                power += (cs[i] - '0') * POWER_LIST[i];
            }
        }
        return cs[cs.length - 1] == PARITYBIT[power % 11];
    }
    
    /**
     * 利用Calendar的宽松模式关闭特性验证真实日历日期
     */
    public static boolean isValidDate(String inDate) {
        if (inDate == null) return false;
        SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd");
        if (inDate.trim().length() != 8) return false;
        dataFormat.setLenient(false);
        try {
            dataFormat.parse(inDate.trim());
        } catch (ParseException e) {
            return false;
        }
        return true;
    }
...

第三步:辅助功能扩展

除了基础校验,该工具还包含了从身份证号中提取出生日期、性别,以及将15位旧证转换为18位新证的实用方法,极大提升了复用性。

边输入边校验的实时处理方案

在网页或App表单场景中,通常需要在用户每输入一位时即时提示是否正确。这种场景无法等待完整的18位输入结束,因此必须采用分段正则匹配策略。以下是针对动态输入优化的校验逻辑核心片段:

操作要点:

第一段:前6位地区码快速过滤

直接比对预定义的 Hashtable 字典。如果前几位组合不在合法列表中,立即返回无效,减少后端交互压力。

第二段:年份部分的渐进匹配

年份的第一位只能是1或2;第二位跟随第一位变化(如1开头只能接9或0)。通过 verifyIndex 方法配合动态生成的正则表达式进行逐字拦截。

第三段:月份与日期的逻辑联动

随着月份数字的确定,日份允许的最大值动态改变。例如选中月份为2时,需进一步判断是否为闰年来决定日份限制是0-8还是0-9。此处逻辑较为复杂,建议封装专用的天数计算方法 dayMonth。

/**
 * 简化版的实时验证逻辑示例
 */
public static boolean idCardValidate(String idStr) {
    int index = 1;
    // 检查首位不为0
    if (idStr.length() >= index && idStr.indexOf("0") == 0) return false;
    index++;
    // 检查地区码是否存在
    Hashtable h = GetAreaCode();
    if (idStr.length() >= index && h.get(idStr.substring(0, index)) == null) return false;
    
    // ... 中间为复杂的年份正则推断逻辑 ... 
    
    // 检查总长度与字符组成
    String filter = "[0-9]{0,17}";
    boolean flag = idStr.matches(filter + (idStr.length() == 18 ? "[0-9,x,X]" : ""));
    return flag;
}

总结

验证身份证号不仅需要符合基本的正则格式,更需要深入到位权计算、历法逻辑和行政编码体系的底层校验。在实际项目中,建议将静态校验工具类集成到公共服务层,而将实时输入校验封装在前端组件库中,双管齐下确保数据的绝对准确性。 以上就是Java验证身份证号码有效性的详细内容,更多关于Java身份证号码校验的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表