技术概述
目前我国身份证号是由18位组成的:其中前6位是地址码,第7至14位是出生日期,第15至第17位是顺序码,它是县、区级政府所辖派出所的分配码。同时第17位也是校验性别码,奇数为男,偶数为女,第18位为校验码。
那么是如何计算第18位校验码的呢?
首先
1、将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2。
2、将这17位数字和系数相乘的结果相加。
3、用加出来和除以11,看余数。
4、余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字。其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2(即余数0对应1,余数1对应0,余数2对应X...)。
5、通过上面得知如果余数是3,就会在身份证的第18位数字上出现的是9。如果对应的数字是2,身份证的最后一位号码就是罗马数字X。
接下来上代码!!!
代码内容
1、ReadIdCardInformation 身份信息实体类
import lombok.Data;
@Data
public class ReadIdCardInformation {
private String birth;
private String age;
private String sex;
private String openFile;
private String grant;
}
2、首先是 controller 文件内容
@GetMapping("/identityInformation")
@ApiOperation(notes = "identityInformation",value = "获取身份证信息")
public RestResponse identityInformation(@ApiParam(name = "identityCard",value = "身份证号")@RequestParam("identityCard") String identityCard){
ReadIdCardInformation readIdCardInformation = toolService.identityInformation(identityCard);
return RestResponse.ok(readIdCardInformation);
}
3、service文件内容 编写逻辑内容 调用工具类 我将身份证对应的编码存入到了数据库中,下发有sql 下载链接
public class ToolService {
@Autowired
private ReadIdCardInformationUtil readIdCardInformationUtil;
@Autowired
private ProvinceCityDistrictService provinceCityDistrictService;
/**
* 获取身份证信息
* @param identityCard
* @return
*/
public ReadIdCardInformation identityInformation(String identityCard){
//验证身份证是否有效
boolean flag = readIdCardInformationUtil.checkIdentityCode(identityCard);
if(!flag){
return null;
}else {
ReadIdCardInformation readIdCardInformation = new ReadIdCardInformation();
//获取出生日期
readIdCardInformation.setBirth(readIdCardInformationUtil.getBirth(identityCard));
//计算年龄
readIdCardInformation.setAge(readIdCardInformationUtil.getAge(identityCard));
//获取性别
readIdCardInformation.setSex(readIdCardInformationUtil.getSex(identityCard));
//获取出生地
String province = identityCard.substring(0,2); // 获取省
ProvinceCityDistrict provinceCityDistrict = new ProvinceCityDistrict();
provinceCityDistrict = provinceCityDistrictService.pcdAccurate(Integer.valueOf(province));
if(provinceCityDistrict.getName() == null){
readIdCardInformation.setOpenFile("无地址,请查询数据");
}else {
province = provinceCityDistrict.getName();
String city = identityCard.substring(0,4);//获取市
provinceCityDistrict = provinceCityDistrictService.pcdAccurate(Integer.valueOf(city));
if(provinceCityDistrict.getName() == null){
readIdCardInformation.setOpenFile(province+"省");
}else {
city = provinceCityDistrict.getName();
String district = identityCard.substring(0,6);//获取区
provinceCityDistrict = provinceCityDistrictService.pcdAccurate(Integer.valueOf(district));
if(provinceCityDistrict.getName() == null){
readIdCardInformation.setOpenFile(province+"省"+city+"市");
}else {
district = provinceCityDistrict.getName();
readIdCardInformation.setOpenFile(province+"省"+city+"市"+district);
}
}
}
//获取发放身份证地点
readIdCardInformation.setGrant(readIdCardInformationUtil.getGrant(identityCard));
return readIdCardInformation;
}
}
}
4、ReadIdCardInformationUtil 工具类,里面装有关于身份证号解析
/**
* 身份证信息读取
*/
@Slf4j
@Component
public class ReadIdCardInformationUtil {
/**
* 身份证号校验
*/
public boolean checkIdentityCode(String identityCode) {
//通过正则校验身份证号格式是否正确
if (!identityCode.matches("^[1-9]\\d{5}([1-9][1-9])\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$")) {
return false;
}
if (!ReadIdCardInformationUtil.birthVerification(identityCode)) {
return false;
}
// 计算最后一位校验码 本体码各位数字乘以对应加权因子并求和 除以11得到余数是校验码 idcard是身份证
// 本体码
String code = identityCode.substring(0, 17);
// 转换成数组
int[] IDnums = new int[code.length()];
for (int i = 0; i < code.length(); i++) {
IDnums[i] = Integer.parseInt(String.valueOf(code.charAt(i)));
}
// 加权因子
int[] x = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
// 对应字符
char[] checkCode = {'1', '0', 'x', '9', '8', '7', '6', '5', '4', '3', '2'};
int sum = 0;
for (int i = 0; i < code.length(); i++) {
int n = code.charAt(i) - 48;
sum = sum + n * x[i];
}
sum = sum % 11;
// 验证最后一位校验码和计算出来的不匹配则不同过验证
String identityCode18 = identityCode.substring(17, 18).toLowerCase();
//如果为大写 X 则转换成小写 x进行比对
if (!identityCode18.equals(String.valueOf(checkCode[sum]))){
log.info(String.valueOf(checkCode[sum]));
log.info("该用户身份证号码不正确,第18位校验码错误");
return false;
}
return true;
}
/**
* 校验出生日期 是符合出生日期格式
*
* @param birthDate
* @return
*/
public static boolean birthVerification(String birthDate) {
Date d = new Date();
DateFormat df = new SimpleDateFormat("yyyyMMdd");
String currentDate = df.format(d);
//验证出生年份不可大于当前年份
if (Integer.parseInt(birthDate.substring(6, 10)) <= Integer.parseInt(currentDate.substring(0, 4)) &&
Integer.parseInt(birthDate.substring(10, 12)) <= Integer.parseInt(currentDate.substring(4, 6)) &&
Integer.parseInt(birthDate.substring(12, 14)) <= Integer.parseInt(currentDate.substring(6, 8))) {
return true;
}
log.info("该用户身份证号码不正确,还没有出生");
return false;
}
/**
* 获取性别
*
* @param sex
* @return
*/
public String getSex(String sex) {
String usex = sex.substring(14, 17);
if (Integer.parseInt(usex) % 2 == 0) {
return "女";
} else {
return "男";
}
}
/**
* 获取生日
*
* @param id
* @return
*/
public String getBirth(String id) {
String birth = id.substring(6, 10) + "年" + id.substring(10, 12) + "月" + id.substring(12, 14) + "日";
return birth;
}
/**
* 获取出生年龄
*
* @param identityCardAge
* @return
*/
public String getAge(String identityCardAge) {
//获取当前年份
int year = Calendar.getInstance().get(Calendar.YEAR);
String age = (year - Integer.parseInt(identityCardAge.substring(6, 10))) + "";
return age;
}
/**
* 获取身份证顺序号
* @param identityCardAge
* @return
*/
public String getGrant(String identityCardAge) {
String grant = identityCardAge.substring(14, 17);//发放地点
return grant;
}
}
5、ProvinceCityDistrictService 用于获取数据库中对应区域名称 逻辑层
import cn.pl.demon.dto.mapper.ProvinceCityDistrictMapper;
import cn.pl.demon.entity.ProvinceCityDistrict;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Random;
@Service
@Slf4j
public class ProvinceCityDistrictService {
@Autowired
private ProvinceCityDistrictMapper provinceCityDistrictMapper;
/**
* 省市区查询
* @param id
* @return
*/
public ProvinceCityDistrict pcdAccurate(Integer id){
ProvinceCityDistrict provinceCityDistrict = provinceCityDistrictMapper.pcdAccurate(id);
return provinceCityDistrict;
}
}
6、ProvinceCityDistrictMapper 数据层
import cn.pl.demon.entity.ProvinceCityDistrict;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface ProvinceCityDistrictMapper {
/**
* 省市区查询
* @param id
* @return
*/
ProvinceCityDistrict pcdAccurate(Integer id);
}
7、最后就是对应的数据库了,我将数据库sql 放到了百度网盘中,下面是链接:
链接: https://pan.baidu.com/s/15Tk7Pdg-VHlbphCCOWCu3A 密码: 62ej
技术使用过程中遇到的问题以及解决过程
其实获取证件号组成信息倒不是很复杂,我主要是浪费时间在编写 验证“证件号”正则表达式上门了,头秃 ,另一个难点就是证件号最后一位校验码的算法了,如何校验已经在上面表述过了。
技术总结
总体来看这个工具类型并不是多么复杂,基本上用的都是Java的基础知识,最后还是要说基础很重要!!!
最后本人是个小萌新,请大佬们多多指教!!!!!