go中国家新闻出版署实名认证

随着网络游戏防沉迷系统的接入,实名认证验证也是一个必不可少的部分,网上有第三方的实名认证但基本上都收费,国家新闻出版署提供了免费的接口,不过需要自己去提交申请,获取应用标识 (APPID)应用密钥 (Secret Key)业务权限标识(BizId),并将请求服务器的IP设为白名单后,就可以调用https:// api.wlc.nppa.gov.cn/idcard/authentication/check接口来获取实名认真了。

这个接口中请求体body使用了AES-128/GCM + BASE64算法加密,签名使用了sha256。直接上完整的代码

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "crypto/sha256"
    "encoding/base64"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "sort"
    "strconv"
    "time"
)

// 国家新闻出版署配置信息
type nppaInfo struct {
    AppId     string // 应用标识
    SecretKey string // 应用密钥
    BizId     string // 业务权限标识
}

var nppa nppaInfo

// 输入国家新闻出版署配置信息
func NppaInit(appId, secretKey, bizId string) {
    nppa = nppaInfo{
        AppId:     appId,
        SecretKey: secretKey,
        BizId:     bizId,
    }
}

// 实名验证
// ai     - 游戏内部成员标识,固定32位字符,一般使用 md5(用户ID)
// name - 实名信息中的姓名
// id     - 实名信息中身份证号码
// 返回
//         - result 是否验证成功
//         - errcode 状态码
//         - errmsg 状态描述
func NppaCheck(ai, name, id string) (result bool, errcode int, errmsg string ) {
    client := &http.Client{}

    // body 参数
    param := map[string]string{
        "ai":    ai,
        "name":  name,
        "idNum": id,
    }

    // AES-128/GCM + BASE64算法加密
    jsonParam, _ := json.Marshal(param)
    cipher, _ := gcmEncrypt(string(jsonParam))
    body, _ := json.Marshal(map[string]string{
        "data": cipher,
    })

    // post请求地址
    req, _ := http.NewRequest("POST", "https://api.wlc.nppa.gov.cn/idcard/authentication/check", bytes.NewReader(body))

    // header头验证信息
    headers := map[string]string{
        "appId":      nppa.AppId,
        "bizId":      nppa.BizId,
        "timestamps": strconv.FormatInt(time.Now().UnixNano()/1e6, 10),
    }

    // 签名
    headers["sign"] = sign(headers, string(body))
    headers["Content-Type"] = "application/json;charset=utf-8"
    for k, v := range headers {
        req.Header.Set(k, v)
    }

    // 请求
    resp, err := client.Do(req)
    if err != nil {
        return false, -1, "Http请求错误"
    }
    defer resp.Body.Close()
    if resp.StatusCode != 200 {
        return false, resp.StatusCode, "Http请求返回码错误"
    }

    str, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return false, -2, "Http请求返回数据为空"
    }

    var msg map[string]interface{}
    err = json.Unmarshal(str, &msg)
    if err != nil {
        return false, -3, "Http请求返回数据格式错误"
    }

    ec, ok := msg["errcode"]
    if !ok {
        return false, -3, "Http请求返回数据格式错误"
    }

    errcode = int(ec.(float64))
    if errcode != 0 {
        errmsg, _:= msg["errmsg"]
        return false, errcode, errmsg.(string)
    }

    data, ok := msg["data"]
    if !ok {
        return false, -3, "Http请求返回数据格式错误"
    }

    r, ok := data.(map[string]interface{})["result"]
    if !ok {
        return false, -3, "Http请求返回数据格式错误"
    }

    status, ok := r.(map[string]interface{})["status"]
    if !ok {
        return false, -3, "Http请求返回数据格式错误"
    }

    errcode = int(status.(float64))
    if errcode == 0{
        return true, 0, "认证成功"
    } else if errcode == 1 {
        return false, errcode, "认证中"
    }else {
        return false, errcode, "认证失败"
    }
}

func gcmEncrypt(originalText string) (string, error) {
    // 需要解码
    key, _ := hex.DecodeString(nppa.SecretKey)
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }

    aesGcm, err := cipher.NewGCM(block)
    if err != nil {
        return "", err
    }

    // 向量
    nonce := make([]byte, aesGcm.NonceSize())
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return "", err
    }

    cipherText := aesGcm.Seal(nonce, nonce, []byte(originalText), nil)

    // encode as base64 string
    encoded := base64.StdEncoding.EncodeToString(cipherText)
    return encoded, nil
}

func sign(headers map[string]string, body string) string {
    var data string
    var keys []string
    // key排序
    for k := range headers {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    // 拼接
    for _, k := range keys {
        data = data + k + headers[k]
    }
    data = nppa.SecretKey + data + body

    // 对字符串进行sha256哈希
    h := sha256.New()
    h.Write([]byte(data))
    sum := h.Sum(nil)
    return hex.EncodeToString(sum)
}

调用

func main() {
    // 初始化
    NppaInit("test-appId","2836e95fcd10e04b0069bb1ee659955b","test-bizId")

    // 使用
    userId := "123456"
    name := "岑吾"
    id := "xxxxxxxxxxxxxxxxxx"
    h := md5.New()
    h.Write([]byte(userId))
    ok,code,msg := NppaCheck(hex.EncodeToString(h.Sum(nil)), name, id)
    fmt.Println(ok,code,msg)
}

详细返回参数可见网络游戏防沉迷实名认证系统

0%