使用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来验证中国大陆、台湾、澳门和香港居民的身份证号码合法性。通过具体的代码示例,详细解释了不同地区身份证号码的校验逻辑。希望这篇文章对您在实际开发中有所帮助。