first commit

This commit is contained in:
Yangtao
2025-11-18 17:48:20 +08:00
commit 6e56cab848
196 changed files with 65809 additions and 0 deletions

View File

@ -0,0 +1,196 @@
package Netease
import (
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"net/url"
"servicebase/pkg/log"
"sort"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/tjfoc/gmsm/sm3"
)
const (
apiURL = "https://verify.dun.163.com/v1/face/liveness/h5/auth"
recheckTokenUrl = "https://verify.dun.163.com/v1/face/liveness/h5/recheck"
version = "v1"
)
type ApplyInfoReq struct {
Name string
CardNo string
RedirectUrl string
CallBackUrl string
DataId string
CallbackValidate string
EncryptType string
}
type BaseRsp[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Result T `json:"result"`
}
type H5ApplyLivePersonRsp struct {
AuthUrl string `json:"authUrl"`
AuthToken string `json:"authToken"`
}
type H5ReCheckLivePersonTokenRsp struct {
TaskId string `json:"taskId"`
PicType int64 `json:"picType"`
Avatar string `json:"avatar"`
Status int64 `json:"status"`
ReasonType int `json:"reasonType"`
IsPayed int64 `json:"isPayed"`
SimilarityScore float64 `json:"similarityScore"`
FaceMatched int64 `json:"faceMatched"`
FaceAttributeInfo interface{} `json:"faceAttributeInfo"`
ExtInfo ExtInfoEntity `json:"extInfo"`
}
type ExtInfoEntity struct {
SuspectedNonageFlag bool `json:"suspectedNonageFlag"`
}
// H5ApplyLivePerson 请求易盾生成h5人身验证h5 url 和 authToken
func H5ApplyLivePerson(ctx context.Context, req *ApplyInfoReq) (*H5ApplyLivePersonRsp, error) {
params := url.Values{
"name": []string{req.Name},
"cardNo": []string{req.CardNo},
"redirectUrl": []string{req.RedirectUrl},
}
rsp, err := apply(params)
if err != nil {
return nil, err
}
return rsp, nil
}
// H5ReCheckLivePersonToken 根据authToken二次验证并获取相关信息如正面拍照的活体图片
func H5ReCheckLivePersonToken(ctx context.Context, token string) (*H5ReCheckLivePersonTokenRsp, error) {
params := url.Values{
"authToken": []string{token},
}
rsp, err := checkToken(params)
if err != nil {
return nil, err
}
return rsp, nil
}
// 生成签名信息
func genSignature(params url.Values) string {
var paramStr string
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
paramStr += key + params[key][0]
}
paramStr += viper.GetString("netease.captcha.secretKey")
if params["signatureMethod"] != nil && params["signatureMethod"][0] == "SM3" {
sm3Reader := sm3.New()
sm3Reader.Write([]byte(paramStr))
return hex.EncodeToString(sm3Reader.Sum(nil))
}
md5Reader := md5.New()
md5Reader.Write([]byte(paramStr))
return hex.EncodeToString(md5Reader.Sum(nil))
}
// 请求易盾接口
func apply(params url.Values) (*H5ApplyLivePersonRsp, error) {
params["secretId"] = []string{viper.GetString("netease.captcha.secretId")}
params["businessId"] = []string{viper.GetString("netease.captcha.businessId")}
params["version"] = []string{version}
params["timestamp"] = []string{strconv.FormatInt(time.Now().UnixNano()/1000000, 10)}
params["nonce"] = []string{strconv.FormatInt(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(10000000000), 10)}
params["signature"] = []string{genSignature(params)}
resp, err := http.Post(apiURL, "application/x-www-form-urlencoded", strings.NewReader(params.Encode()))
if err != nil {
return nil, errors.WithStack(err)
}
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("请求网易易盾获取h5人身验证失败状态码为%d", resp.StatusCode)
}
defer resp.Body.Close()
contents, _ := io.ReadAll(resp.Body)
log.InfoF("请求网易易盾获取h5人身验证响应: %s", string(contents))
var rsp BaseRsp[*H5ApplyLivePersonRsp]
err = json.Unmarshal(contents, &rsp)
if err != nil {
return nil, errors.WithStack(err)
}
if rsp.Code != 200 {
return nil, errors.Errorf("响应体状态码不是200(%d),信息: %s", rsp.Code, rsp.Msg)
}
return rsp.Result, nil
}
func checkToken(params url.Values) (*H5ReCheckLivePersonTokenRsp, error) {
params["secretId"] = []string{viper.GetString("netease.captcha.secretId")}
params["businessId"] = []string{viper.GetString("netease.captcha.businessId")}
params["version"] = []string{version}
params["timestamp"] = []string{strconv.FormatInt(time.Now().UnixNano()/1000000, 10)}
params["nonce"] = []string{strconv.FormatInt(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(10000000000), 10)}
params["signature"] = []string{genSignature(params)}
resp, err := http.Post(recheckTokenUrl, "application/x-www-form-urlencoded", strings.NewReader(params.Encode()))
if err != nil {
return nil, errors.WithStack(err)
}
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("请求网易易盾获取h5人身验证失败状态码为%d", resp.StatusCode)
}
defer resp.Body.Close()
contents, _ := io.ReadAll(resp.Body)
log.InfoF("请求网易云盾获取人身核验二次验证token响应: %s", string(contents))
var rsp BaseRsp[*H5ReCheckLivePersonTokenRsp]
err = json.Unmarshal(contents, &rsp)
if err != nil {
return nil, errors.WithStack(err)
}
if rsp.Code != 200 {
return nil, errors.Errorf("响应体状态码不是200(%d),信息: %s", rsp.Code, rsp.Msg)
}
return rsp.Result, nil
}
var livePersonAndIdCheckFailReason = map[int]string{
2: "活体通过,姓名身份证号一致,人脸比对非同一人",
3: "活体通过,姓名身份证号不一致",
4: "活体不通过",
5: "活体检测超时或出现异常",
6: "活体通过,查无此身份证",
7: "活体通过,库中无此身份证照片",
8: "活体通过,人脸照过大",
9: "活体通过,权威数据源出现异常",
10: "疑似攻击,建议拦截",
11: "检测对象为未成年人",
}
func GetLivePersonAndIdCheckFailReason(code int) string {
r, ok := livePersonAndIdCheckFailReason[code]
if !ok {
return fmt.Sprintf("其他原因ReasonType==%d", code)
}
return r
}