验证身份证号注解
注解
IDCardValid.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.sc.springboot.domain;
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint( validatedBy = IDCardValidator.class ) public @interface IDCardValid { String message() default "身份证号码不正确";
Class<?>[] groups() default {
};
Class<? extends Payload>[] payload() default {
}; }
|
约束类
IDCardValidator.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
| package com.sc.springboot.domain;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.regex.Matcher; import java.util.regex.Pattern;
public class IDCardValidator implements ConstraintValidator<IDCardValid, String> {
private static int[] weightCode = new int[17];
private static final String[] CHECK_CODE = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
private static final int DIVIDER = 11;
private static final int AREA_NUMBER = 6;
private static final String YEAR_PREFIX = "19";
private static final int OLD_ID_CARD_LENGTH = 15;
private static final int NEW_ID_CARD_LENGTH = 18;
private static void setWiBuffer() { for (int i = 0; i < weightCode.length; i++) { int k = (int) Math.pow(2, (weightCode.length - i)); weightCode[i] = (k % DIVIDER); } }
private static boolean checkLengthAndBirthday(final String idCard) { if ((idCard.length() == OLD_ID_CARD_LENGTH) || (idCard.length() == NEW_ID_CARD_LENGTH)) { return regexCheckAndCheckBirthday(idCard); } return false; }
private static boolean regexCheckAndCheckBirthday(final String idCard) { String birthday = "";
if (idCard.length() == OLD_ID_CARD_LENGTH) { if (idCard15RegexCheck(idCard)) { birthday = YEAR_PREFIX + idCard.substring(AREA_NUMBER, AREA_NUMBER + 6); } } else { if (idCard18RegexCheck(idCard)) { birthday = idCard.substring(AREA_NUMBER, AREA_NUMBER + 8); } } return checkStrDate(birthday); }
public static boolean checkIdCard18(final String idCard) { int dividedResult = getDividedResult(idCard); String code = CHECK_CODE[dividedResult]; String lastStr = idCard.substring(idCard.length() - 1); if (code.equals(lastStr)) { return true; } return false; }
public static int getDividedResult(String idCard) { String[] idCardNum = idCard.substring(0, 17).split(""); int sum = 0; for (int i = 0; i < idCardNum.length; i++) { sum += Integer.parseInt(idCardNum[i]) * weightCode[i]; } return sum % DIVIDER; }
private static boolean idCard15RegexCheck(String idCard) { String reg = "^(\\d{8}(0[1-9]|1[012])(0[1-9]|[12]\\d|3[01])\\d{3})$"; Pattern pattern = Pattern.compile(reg); Matcher m = pattern.matcher(idCard); return (m.matches()) ? true : false; }
private static boolean idCard18RegexCheck(String idCard) { String reg = "^(\\d{6}(18|19|20)\\d{2}(0[1-9]|1[012])(0[1-9]|[12]\\d|3[01])\\d{3}(\\d|X|x))$"; Pattern pattern = Pattern.compile(reg); Matcher m = pattern.matcher(idCard); return (m.matches()) ? true : false; }
private static boolean checkStrDate(final String strDate) { try { LocalDate.parse(strDate, DateTimeFormatter.ofPattern("yyyyMMdd")); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
@Override public void initialize(IDCardValid constraintAnnotation) { setWiBuffer(); }
@Override public boolean isValid(String value, ConstraintValidatorContext context) { if (checkLengthAndBirthday(value)) { return value.length() == OLD_ID_CARD_LENGTH || checkIdCard18(value); } return false; } }
|
ConstraintValidatorContext 类 介绍:
1 2 3 4 5 6 7 8 9 10 11 12
| /** * ConstraintValidatorContext 提供了以下功能: * 错误消息:通过 ConstraintValidatorContext 可以添加自定义的错误消息,将验证失败的详细信息报告给调用方。 * 例如,可以使用 context.buildConstraintViolationWithTemplate("自定义错误消息").addConstraintViolation() 方法, * 在验证失败时添加一个自定义的错误消息。 * 禁用默认错误消息:可以使用 context.disableDefaultConstraintViolation() 方法禁用默认的错误消息, * 从而完全控制错误消息的生成和显示。 * 属性节点路径:ConstraintValidatorContext 可以跟踪验证过程中的属性节点路径,以便在错误消息中可以包含属性的层级关系。 * 例如,可以使用 context.buildConstraintViolationWithTemplate("属性A的值无效") * .addPropertyNode("A").addConstraintViolation() 方法,在错误消息中指定属性 A 的路径和错误信息。 * 违反的约束:通过 context.buildConstraintViolationWithTemplate() 方法,可以指定正在验证的约束(注解)及其对应的错误消息。 */
|
验证是否有效
IDCardValidatorTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| package com.sc.springboot;
import javax.validation.*;
import com.sc.springboot.domain.IDCardValid; import lombok.extern.slf4j.Slf4j; import org.junit.BeforeClass; import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.*;
@Slf4j public class IDCardValidatorTest { private static Validator validator;
@BeforeClass public static void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); }
private static class TestObject { @IDCardValid private String idCardNumber;
public String getIdCardNumber() { return idCardNumber; }
public void setIdCardNumber(String idCardNumber) { this.idCardNumber = idCardNumber; } }
@Test public void testValidIDCardNumber() { TestObject testObject = new TestObject(); testObject.setIdCardNumber("520201197209083216");
Set<ConstraintViolation<TestObject>> violations = validator.validate(testObject); assertEquals(0, violations.size());
}
@Test public void testInvalidIDCardNumber() { TestObject testObject = new TestObject(); testObject.setIdCardNumber("36102120001107201");
Set<ConstraintViolation<TestObject>> violations = validator.validate(testObject); assertEquals(1, violations.size()); ConstraintViolation<TestObject> violation = violations.iterator().next(); log.error(violation.getMessage()); log.error(violation.getPropertyPath().toString()); } }
|
assertEquals方法介绍:
assertEquals
是一种断言方法,通常用于单元测试中。它用于验证实际值和期望值是否相等。
在测试中,您可以使用 assertEquals
来比较两个值是否相等。如果实际值和期望值不相等,该断言将会失败,并会生成一个错误报告。
assertEquals
方法通常具有以下形式:
1
| assertEquals(expected, actual);
|
其中:
expected
是期望的值,即我们希望得到的结果。
actual
是实际的值,即我们需要验证的实际结果。
如果 expected
和 actual
相等,那么断言成功,测试继续执行;如果它们不相等,断言失败,测试将停止并抛出错误。
Violations介绍
1
| Set<ConstraintViolation<TestObject>> violations = validator.validate(testObject);
|
这段代码是使用JSR 303/349标准定义的Bean验证(Bean Validation)框架来验证一个名为testObject
的对象。具体来说,它包含以下几个步骤:
validator.validate(testObject)
:通过调用validate
方法来执行验证操作。validator
是已经初始化好的验证器对象,testObject
是要验证的目标对象。
Set<ConstraintViolation<TestObject>> violations
:validate
方法返回一个Set
集合,其中包含了所有验证失败的结果。ConstraintViolation
是验证失败的详细信息对象,它包含了验证失败的字段、对应的值、以及验证失败的原因等信息。
通过对violations
进行处理,你可以获取验证失败的详细信息,并根据需要进行相应的处理,例如输出错误消息、记录日志等。
需要注意的是,在使用这段代码之前,你需要确保已经初始化了validator
对象,并且已经将相关的验证约束(如注解)添加到了TestObject
类的相应字段或方法上,以便进行验证。