随着网络游戏防沉迷系统的接入,实名认证验证也是一个必不可少的部分,网上有第三方的实名认证但基本上都收费,国家新闻出版署提供了免费的接口,不过需要自己去提交申请,获取应用标识 (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)
}
详细返回参数可见网络游戏防沉迷实名认证系统