欢迎您访问如何自定义@IdCard注解校验身份证号码格式并与Validation兼容!

如何自定义@IdCard注解校验身份证号码格式并与Validation兼容

更新时间:2025-04-27 15:06:04作者:佚名

目标

自定义一个用于校准身分证号码格式的注解@IdCard,才能和现有的Validation参数校准机制兼容,使用方法和其他校准注解保持一致(使用@Valid注解插口参数)。

本文使用原生形式实现校准逻辑,校准规则的实现较为基础;Hutool工具提供了愈加建立的校准工具,可以考虑使用其来实现校准逻辑。

使用Hutool身分证号码格式校准工具,实现校准逻辑,参考博客如下:

《身份证号码,格式校准:@IdCard(Validation+Hutool)》

校准逻辑有效格式

符合国家标准。

公民身分号码依照GB11643-1999《公民身分号码》国家标准编制,由18位数字组成:前6位为行政区划代码,第7至14位为出生日期码,第15至17位为次序码,第18位为校准码。

严格校准

本文采用的校准方法,采用严格校准,第18位校准码,只能为数字或小写X,大写x未能通过校准。

不校准非空

身分证号码注解,校准的是格式;不校准是否为空(null或空字符串)。假如身分证号码为空,此注解校准是可以通过的;

是否校准非空,要依照业务逻辑来确定;假如业务逻辑须要校准非空,则使用注解@NotEmpty。

核心代码

须要定义的内容包含三个部份:

注解@IdCard校准器IdCardValidator校准工具类IdCardUtil注解:@IdCard

package com.example.core.validation.idcard;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
 * 身份证号码。字符串必须是格式正确的身份证号码。
 * 

* {@code null} 或 空字符串,是有效的(能够通过校验)。 *

* 支持的类型:字符串 * * @author songguanxun * @since 1.0 */ @Target({FIELD}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = IdCardValidator.class) public @interface IdCard { /** * @return the error message template */ String message() default "身份证号码,格式错误"; /** * @return the groups the constraint belongs to */ Class<?>[] groups() default {}; /** * @return the payload associated to the constraint */ Class<? extends Payload>[] payload() default {}; }

校准器:IdCardValidator

package com.example.core.validation.idcard;
import com.example.core.util.IdCardUtil;
import org.springframework.util.ObjectUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
 * 身份证号码,格式校验器
 */
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
    @Override
    public void initialize(IdCard constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (ObjectUtils.isEmpty(value)) {
            return true;
        }
        return IdCardUtil.isValid(value);
    }
}

校准工具类

package com.example.core.util;
/**
 * 身份证号码,校验工具类
 */
public class IdCardUtil {
    // 每位加权因子
    private static final int[] power = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    /**
     * 是格式正确的身份证号码
     */
    public static boolean isValid(String idCard) {
        // null ,为假
        if (idCard == null) {
            return false;
        }
        // 非18位,为假
        if (idCard.length() != 18) {
            return false;
        }
        // 获取前17位
        String idCard17 = idCard.substring(0, 17);
        // 获取第18位
        String idCard18Code = idCard.substring(17, 18);
        // 前17位,不全部为数字,为假
        if (!isDigital(idCard17)) {
            return false;
        }
        char[] c = idCard17.toCharArray();
        int[] bit = convertCharToInt(c);
        int sum17 = getPowerSum(bit);
        // 将和值与11取模得到余数进行校验码判断
        String checkCode = getCheckCodeBySum(sum17);
        if (null == checkCode) {
            return false;
        }
        // 将身份证的第18位,与算出来的校码进行匹配,不相等就为假
        return idCard18Code.equals(checkCode);
    }
    /**
     * 数字验证
     */
    private static boolean isDigital(String str) {
        return str != null && !str.isEmpty() && str.matches("^[0-9]*$");
    }
    /**
     * 将字符数组转为整型数组
     */
    private static int[] convertCharToInt(char[] c) throws NumberFormatException {
        int[] a = new int[c.length];
        int k = 0;
        for (char temp : c) {
            a[k++] = Integer.parseInt(String.valueOf(temp));
        }
        return a;
    }
    /**
     * 将身份证的每位和对应位的加权因子相乘之后,再得到和值
     */
    private static int getPowerSum(int[] bit) {
        if (power.length != bit.length) {
            return 0;
        }
        int sum = 0;
        for (int i = 0; i < bit.length; i++) {
            for (int j = 0; j < power.length; j++) {
                if (i == j) {
                    sum = sum + bit[i] * power[j];
                }
            }
        }
        return sum;
    }
    /**
     * 将和值与11取模得到余数进行校验码判断
     *
     * @return 校验位
     */
    private static String getCheckCodeBySum(int sum17) {
        String checkCode = null;
        switch (sum17 % 11) {
            case 10:
                checkCode = "2";
                break;
            case 9:
                checkCode = "3";
                break;
            case 8:
                checkCode = "4";
                break;
            case 7:
                checkCode = "5";
                break;
            case 6:
                checkCode = "6";
                break;
            case 5:
                checkCode = "7";
                break;
            case 4:
                checkCode = "8";
                break;
            case 3:
                checkCode = "9";
                break;
            case 2:
                checkCode = "X";
                break;
            case 1:
                checkCode = "0";
                break;
            case 0:
                checkCode = "1";
                break;
        }
        return checkCode;
    }
}

使用

@IdCard置于须要校准格式的身分证号码数组上。

package com.example.web.response.model.param;
import com.example.core.validation.idcard.IdCard;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "新增用户Param")
public class UserAddParam {
    
    // 其他字段
    @IdCard
    @Schema(description = "身份证号码", example = "110101202301024130")
    private String idCard;
}

校准疗效校准工具类,单元测试

package com.example;
import com.example.core.util.IdCardUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class IdCardTest {
    @Test
    void test() {
        test("110101202301024130");
        test("11010120230102857X");
        test("11010120230102857x");
        test("110101202301024130啊啊啊啊");
    }
    private void test(String idCard) {
        log.info("是否为身份证号码格式:{} = {}", idCard, IdCardUtil.isValid(idCard));
    }
}

插口测试校准结果为成功

校准结果为失败

为您推荐

兰州市教育局:2023湖南长沙高考成绩查询入口、查分网站

2023年甘肃兰州中考成绩查询通常需要到学校或者教育局进行查询,但是现在随着科技的发展,中考成绩查询已经变得更加方便快捷,多个渠道都可以查询导中考成绩。考生可关注兰州市教育局“官方公众号”,考生和家长可通过微信公众号提供的2023年甘肃兰州中考成绩查询入口进行查询。

2023-09-06 22:44

思明区2021年冬季高中招生划片方案公布!

年秋季我区小学招生工作制定如下工作意见。小学招生片区由区教育局划定并提前向社会公布。所:思明小学、金鸡亭小学,招生对象的界定参照入学矛盾特别突出的“热点学校”相关条款执行。年秋季小学招收三类残疾儿童工作意见》(《思明区2021年秋季小学招收三类残疾儿童工作意见》)。区教育局将根据民办小学定级评估结果,合理核定各民办小学招生计划。思明区2021年秋季小学思明区2021年秋季小学招生

2023-09-06 21:19

2021年西安中学分校排行

2021年兰州初中学校排名一、2021年兰州初中学校排名【2021年兰州初中学校排名】相关文章:2021沈阳初中学校排名10-132021无锡初中学校排名07-102021兰州大学全国排名06-28兰州城市学院全国排名,2018兰州城市学院排名及分数线12-10兰州工业学院全国排名,2018兰州工业学院排名及分数线12-19

2023-09-06 17:41

2022北京越秀区高中招生新政汇总

2015年最新广州重点小学排名一览广州市重点小学招生地段一览广州省级小学一览表2017年最新广州重点小学排名一览广州白云区小学招生地段2016年广州白云区小学招生地段范围一览表2016年天河区公办小学报名地段及招生计划表公布,那么2016年2广州天河区各小学招生地段范围如何安排?

2023-09-06 16:57

最新!2020年重庆相城区中学热度排行!

2020年苏州中考结束后,家长对于苏州初中排名更加关注了。2020年苏州姑苏区初中热度排名!我们先来看一下吴中区整体的热度排名表。相比较姑苏区,吴中区的热门初中的数量是少了一些。2020年吴中区的中考人数与19年相比,增加了1379人,比姑苏区增长的人数还要多。结合上述所有维度,2020年苏州吴中区的初中实力榜单分布如下:普通班(地段入学):往年中考成绩在吴中区比较靠前

2023-09-06 15:28

2022龙泉驿区各中学,逐个盘

4、成华7片对口初中减少:列五中学双桥校区。6、成华10片对口初中减少:列五中学双桥校区。7、划片增加:桂林小学对口石室初中华青学校。成华小学,成华区老牌名校,创建于1991年,是成都市首批“九年义务教育示范学校”,也是成都市第一批义务教育名校集团之一,包括成华小学、成华小学南区、成华小学西区三个校区及甘孜州泸定县成武小学校点。

2023-09-06 13:23

加载中...