使用Spring Boot实现居民身份证合法性验证

使用Spring Boot实现居民身份证合法性验证

在现代社会中,身份证号码的合法性验证是很多系统中不可或缺的一部分。身份证号码用于确认个人身份,其格式和校验机制各不相同。本文将介绍如何使用Spring Boot构建一个通用控制器,通过API来验证中国大陆、台湾、澳门和香港居民的身份证号码合法性。

代码概述

本文的核心代码是一个Spring Boot控制器CommonController,它提供了一个API用于验证身份证号码的合法性。以下是该控制器的完整代码:

java 复制代码
@SuppressWarnings("AlibabaUndefineMagicConstant")
@Tag(name = "通用控制器", description = "通用控制器")
@RestController
@RequestMapping("/common")
public class CommonController {

    @Autowired
    private ICommonService commonService;

    /**
     * 每一位的权重
     */
    private static final int[] WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    /**
     * 校验码对应表
     */
    private static final char[] CHECK_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

    /**
     * 正则表达式匹配澳门身份证号码
     */
    private static final Pattern MACAU_ID_PATTERN = Pattern.compile("[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?");

    /**
     * 居民身份证合法性验证
     *
     * @param idCard 身份证号码
     * @return ValidationResultModel
     */
    @Operation(summary = "居民身份证合法性验证", description = "居民身份证合法性验证")
    @Parameter(name = "idCard", description = "居民身份证号码", required = true, in = ParameterIn.QUERY)
    @GetMapping("/validateID")
    public ApiResult<ValidationResultModel> validateIdCard(@RequestParam("idCard") String idCard) throws ParseException {
        IDCardType idCardType = determineIdCardType(idCard);
        switch (idCardType) {
            case MAINLAND:
                return validateMainlandId(idCard);
            case TAIWAN:
                return validateTaiwanId(idCard);
            case MACAU:
                return validateMacauId(idCard);
            case HONGKONG:
                return validateHongKongId(idCard);
            default:
                ValidationResultModel invalid = new ValidationResultModel();
                invalid.setValid(false);
                invalid.setMessage("身份证件号码无效,请仔细核对后重新输入!");
                return ApiResult.ok(invalid);
        }
    }

    private IDCardType determineIdCardType(String idCard) {
        if (idCard.matches("[1-9][0-9]{5}(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[0-9]{3}[0-9Xx]")) {
            return IDCardType.MAINLAND;
        } else if (idCard.matches("[A-Z][0-9]{9}")) {
            return IDCardType.TAIWAN;
        } else if (idCard.matches("[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?")) {
            return IDCardType.MACAU;
        } else if (idCard.matches("[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?")) {
            return IDCardType.HONGKONG;
        } else {
            return IDCardType.UNKNOWN;
        }
    }

    enum IDCardType {
        MAINLAND,
        TAIWAN,
        MACAU,
        HONGKONG,
        UNKNOWN
    }

    private static char getCheckCode(String idCard17) {
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += (idCard17.charAt(i) - '0') * WEIGHT[i];
        }
        return CHECK_CODE[sum % 11];
    }

    public boolean validateTaiwanId(String id) {
        if (id == null || !id.matches("[A-Z][0-9]{9}")) {
            return false;
        }

        char letter = id.charAt(0);
        int letterValue = LETTER_TO_NUMBER_MAP.get(letter);

        int letter1 = letterValue / 10;
        int letter2 = letterValue % 10;

        int sum = letter1 + letter2 * 9;
        int[] weights = {8, 7, 6, 5, 4, 3, 2, 1};
        for (int i = 0; i < 8; i++) {
            sum += Character.getNumericValue(id.charAt(i + 1)) * weights[i];
        }
        sum += Character.getNumericValue(id.charAt(9));
        return sum % 10 == 0;
    }

    private static final Map<Character, Integer> LETTER_TO_NUMBER_MAP = new HashMap<>();

    static {
        LETTER_TO_NUMBER_MAP.put('A', 10);
        LETTER_TO_NUMBER_MAP.put('B', 11);
        LETTER_TO_NUMBER_MAP.put('C', 12);
        LETTER_TO_NUMBER_MAP.put('D', 13);
        LETTER_TO_NUMBER_MAP.put('E', 14);
        LETTER_TO_NUMBER_MAP.put('F', 15);
        LETTER_TO_NUMBER_MAP.put('G', 16);
        LETTER_TO_NUMBER_MAP.put('H', 17);
        LETTER_TO_NUMBER_MAP.put('J', 18);
        LETTER_TO_NUMBER_MAP.put('K', 19);
        LETTER_TO_NUMBER_MAP.put('L', 20);
        LETTER_TO_NUMBER_MAP.put('M', 21);
        LETTER_TO_NUMBER_MAP.put('N', 22);
        LETTER_TO_NUMBER_MAP.put('P', 23);
        LETTER_TO_NUMBER_MAP.put('Q', 24);
        LETTER_TO_NUMBER_MAP.put('R', 25);
        LETTER_TO_NUMBER_MAP.put('S', 26);
        LETTER_TO_NUMBER_MAP.put('T', 27);
        LETTER_TO_NUMBER_MAP.put('U', 28);
        LETTER_TO_NUMBER_MAP.put('V', 29);
        LETTER_TO_NUMBER_MAP.put('W', 30);
        LETTER_TO_NUMBER_MAP.put('X', 31);
        LETTER_TO_NUMBER_MAP.put('Y', 32);
        LETTER_TO_NUMBER_MAP.put('Z', 33);
    }

    public static boolean validateMacauId(String id) {
        if (id == null || !MACAU_ID_PATTERN.matcher(id).matches()) {
            return false;
        }

        int[] weights = {8, 7, 6, 5, 4, 3, 2, 1};
        int sum = 0;
        for (int i = 0; i < 7; i++) {
            char c = id.charAt(i);
            sum += Character.getNumericValue(c) * weights[i];
        }

        char checkChar = id.charAt(7);
        int checkCode;
        if (checkChar == 'A') {
            checkCode = 10;
        } else {
            checkCode = Character.getNumericValue(checkChar);
        }

        return (sum + checkCode) % 11 == 0;
    }

    public static boolean validateHongKongId(String id) {
        if (id == null || !id.matches("[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?")) {
            return false;
        }
        id = id.replace("(", "").replace(")", "");

        if (id.length() == 8) {
            id = " " + id;
        }

        if (id.length() != 9) {
            return false;
        }

        int[] weights = {9, 8, 7, 6, 5, 4, 3, 2, 1};
        int total = 0;
        for (int i = 0; i < 9; i++) {
            total += charToValue(id.charAt(i)) * weights[i];
        }

        int remainder = total % 11;
        char checkDigit = id.charAt(id.length() - 1);

        if (remainder == 0) {
            return checkDigit == '0';
        } else if (remainder == 1) {
            return Character.toUpperCase(checkDigit) == 'A';
        } else {
            return checkDigit == Character.forDigit(11 - remainder, 10);
        }
    }

    private static int charToValue(char c) {
        if (c == ' ') {
            return 36;
        } else if (Character.isLetter(c)) {
            return Character.toUpperCase(c) - 55;
        } else {
            return Character.getNumericValue(c);
        }
    }
}

功能解析

1. 主要组件

注解
  • @Tag:用于Swagger文档生成,定义了控制器的名称和描述。
  • @RestController:表明该类是一个控制器,并且每个方法都会返回一个对象,这些对象会自动转换为JSON格式。
  • @RequestMapping:定义了基础路径为/common,即所有的请求都以该

路径为前缀。

自动注入
  • @Autowired:自动注入了一个服务接口ICommonService,用于业务逻辑的处理。

2. 常量

身份证校验相关常量
  • WEIGHT:大陆居民身份证每位数字的权重,用于校验码计算。
  • CHECK_CODE:校验码对照表,根据前17位的计算结果获取校验码。
  • MACAU_ID_PATTERN:用于匹配澳门身份证号码的正则表达式。

3. 方法

validateIdCard
  • 接收一个身份证号码,并根据其类型调用相应的校验方法。
  • 校验类型包括大陆、台湾、澳门、香港的身份证号码。
determineIdCardType
  • 根据身份证号码的格式判断其类型(大陆、台湾、澳门、香港、未知)。

4. 身份证校验方法

  • getCheckCode:计算大陆居民身份证前17位的校验码。
  • validateTaiwanId:校验台湾身份证号码是否合法。
  • validateMacauId:校验澳门身份证号码是否合法。
  • validateHongKongId:校验香港身份证号码是否合法。

详细说明

居民身份证合法性验证

大陆身份证

大陆居民身份证号码为18位数字,前17位为出生日期、地区代码等信息,第18位为校验码。校验码根据前17位数字计算得出:

java 复制代码
private static char getCheckCode(String idCard17) {
    int sum = 0;
    for (int i = 0; i < 17; i++) {
        sum += (idCard17.charAt(i) - '0') * WEIGHT[i];
    }
    return CHECK_CODE[sum % 11];
}
台湾身份证

台湾身份证号码为10位,第一位为字母,表示地区,后9位为数字。校验时需要将字母转换为对应的数字,并根据特定权重计算总和:

java 复制代码
public boolean validateTaiwanId(String id) {
    if (id == null || !id.matches("[A-Z][0-9]{9}")) {
        return false;
    }

    char letter = id.charAt(0);
    int letterValue = LETTER_TO_NUMBER_MAP.get(letter);

    int letter1 = letterValue / 10;
    int letter2 = letterValue % 10;

    int sum = letter1 + letter2 * 9;
    int[] weights = {8, 7, 6, 5, 4, 3, 2, 1};
    for (int i = 0; i < 8; i++) {
        sum += Character.getNumericValue(id.charAt(i + 1)) * weights[i];
    }
    sum += Character.getNumericValue(id.charAt(9));
    return sum % 10 == 0;
}
澳门身份证

澳门身份证号码为9位,前7位为数字,第8位为校验码。需要根据特定权重计算校验码:

java 复制代码
public static boolean validateMacauId(String id) {
    if (id == null || !MACAU_ID_PATTERN.matcher(id).matches()) {
        return false;
    }

    int[] weights = {8, 7, 6, 5, 4, 3, 2, 1};
    int sum = 0;
    for (int i = 0; i < 7; i++) {
        char c = id.charAt(i);
        sum += Character.getNumericValue(c) * weights[i];
    }

    char checkChar = id.charAt(7);
    int checkCode;
    if (checkChar == 'A') {
        checkCode = 10;
    } else {
        checkCode = Character.getNumericValue(checkChar);
    }

    return (sum + checkCode) % 11 == 0;
}
香港身份证

香港身份证号码由8或9位组成,前1-2位为字母,后6位为数字,最后一位为校验码。需要根据特定权重计算校验码:

java 复制代码
public static boolean validateHongKongId(String id) {
    if (id == null || !id.matches("[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?")) {
        return false;
    }
    id = id.replace("(", "").replace(")", "");

    if (id.length() == 8) {
        id = " " + id;
    }

    if (id.length() != 9) {
        return false;
    }

    int[] weights = {9, 8, 7, 6, 5, 4, 3, 2, 1};
    int total = 0;
    for (int i = 0; i < 9; i++) {
        total += charToValue(id.charAt(i)) * weights[i];
    }

    int remainder = total % 11;
    char checkDigit = id.charAt(id.length() - 1);

    if (remainder == 0) {
        return checkDigit == '0';
    } else if (remainder == 1) {
        return Character.toUpperCase(checkDigit) == 'A';
    } else {
        return checkDigit == Character.forDigit(11 - remainder, 10);
    }
}

总结

本文介绍了如何使用Spring Boot构建一个通用控制器,通过API来验证中国大陆、台湾、澳门和香港居民的身份证号码合法性。通过具体的代码示例,详细解释了不同地区身份证号码的校验逻辑。希望这篇文章对您在实际开发中有所帮助。

相关推荐
qmx_0716 分钟前
HTB-Jerry(tomcat war文件、msfvenom)
java·web安全·网络安全·tomcat
waterHBO24 分钟前
python 爬虫 selenium 笔记
爬虫·python·selenium
为风而战24 分钟前
IIS+Ngnix+Tomcat 部署网站 用IIS实现反向代理
java·tomcat
编程零零七1 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
技术无疆2 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
AIAdvocate3 小时前
Pandas_数据结构详解
数据结构·python·pandas
小言从不摸鱼3 小时前
【AI大模型】ChatGPT模型原理介绍(下)
人工智能·python·深度学习·机器学习·自然语言处理·chatgpt
罗政5 小时前
[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离
vue.js·spring boot·后端
架构文摘JGWZ5 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
FreakStudio5 小时前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
python·嵌入式·面向对象·电子diy