You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
2.6 KiB
82 lines
2.6 KiB
package idverify
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"strings"
|
|
)
|
|
|
|
// 身份证号的本地处理:格式校验、掩码、加盐指纹。
|
|
//
|
|
// 本地校验的意义不只是"友好提示"——服务商核验是**按次计费**的,把明显不合法的号码
|
|
// (位数不对、校验位算不上)挡在调用之前,既省额度也避免把垃圾请求算进失败次数。
|
|
|
|
// 加权因子与校验码表(GB 11643-1999 附录A,ISO 7064:1983 MOD 11-2)。
|
|
var (
|
|
idWeights = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
|
|
idCheckCode = [11]byte{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}
|
|
)
|
|
|
|
// ValidIdCard 校验 18 位二代身份证号:前 17 位为数字,末位为数字或 X/x,且校验位正确。
|
|
// 只支持二代证——阿里云 Id2MetaVerify 与腾讯云 IdCardVerification 都只认二代证。
|
|
func ValidIdCard(no string) bool {
|
|
no = strings.TrimSpace(no)
|
|
if len(no) != 18 {
|
|
return false
|
|
}
|
|
sum := 0
|
|
for i := 0; i < 17; i++ {
|
|
c := no[i]
|
|
if c < '0' || c > '9' {
|
|
return false
|
|
}
|
|
sum += int(c-'0') * idWeights[i]
|
|
}
|
|
last := no[17]
|
|
if last == 'x' {
|
|
last = 'X'
|
|
}
|
|
return last == idCheckCode[sum%11]
|
|
}
|
|
|
|
// MaskIdCard 生成用于展示与落库的掩码:保留前 4 位与后 4 位,中间一律 *。
|
|
// 例:440301199001011234 -> 4403**********1234
|
|
// 非 18 位的输入原样返回掩码化的兜底(全 *),避免意外把原文写进库。
|
|
func MaskIdCard(no string) string {
|
|
no = strings.TrimSpace(no)
|
|
if len(no) != 18 {
|
|
return strings.Repeat("*", len(no))
|
|
}
|
|
return no[:4] + strings.Repeat("*", 10) + no[14:]
|
|
}
|
|
|
|
// HashIdCard 生成加盐 SHA-256 指纹(小写十六进制),用于后台排查"同一证件绑了多个账号"。
|
|
//
|
|
// 必须加盐:身份证号空间有限(约 10^17,且前 6 位地区码、中间 8 位生日高度可枚举),
|
|
// 裸 SHA-256 可以被彻底反查,等同于明文存储。salt 由 ID_HASH_SALT 环境变量提供。
|
|
// salt 为空时返回空串——调用方据此跳过写指纹,绝不退化成裸哈希。
|
|
func HashIdCard(no, salt string) string {
|
|
no = strings.TrimSpace(no)
|
|
if no == "" || salt == "" {
|
|
return ""
|
|
}
|
|
sum := sha256.Sum256([]byte(salt + "|" + strings.ToUpper(no)))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
// GenderFromIdCard 从身份证号解析性别:第 17 位(顺序码末位)奇数为男、偶数为女。
|
|
// 返回值对齐 pb.DBUser.Gender 的口径:1=男,2=女,0=解析不出。
|
|
func GenderFromIdCard(no string) int32 {
|
|
no = strings.TrimSpace(no)
|
|
if len(no) != 18 {
|
|
return 0
|
|
}
|
|
c := no[16]
|
|
if c < '0' || c > '9' {
|
|
return 0
|
|
}
|
|
if (c-'0')%2 == 1 {
|
|
return 1
|
|
}
|
|
return 2
|
|
}
|
|
|