first commit
This commit is contained in:
196
pkg/client/netease/live_person.go
Normal file
196
pkg/client/netease/live_person.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user