国家网络游戏防沉迷实名认证系统 接入核心工具

1. AntiAddictionService 防沉迷系统 业务代码

<?php


namespace App\Services;


use GuzzleHttp\Client;

class AntiAddictionService
{
    private $app_id = '';
    private $secret_key = '';
    private $biz_id = '';

    /**
     * AntiAddictionService constructor.
     * @param $app_id
     * @param $secret_key
     * @param $biz_id
     */
    public function __construct($app_id, $secret_key, $biz_id) {
        $this->app_id = $app_id;
        $this->secret_key = $secret_key;
        $this->biz_id = $biz_id;
    }

    /**
     * 实名认证接口
     * @param $ai
     * @param $name
     * @param $id_num
     */
    public function checkIdCard($ai, $name, $id_num, $test_code = '') {
        $client = new Client();
        $body = [
            'ai' => $ai,
            'name' => $name,
            'idNum' => $id_num
        ];
        $uri = 'https://api.wlc.nppa.gov.cn/idcard/authentication/check';
//        $uri = 'https://wlc.nppa.gov.cn/test/authentication/check';
        if($test_code) {
            $uri = 'https://wlc.nppa.gov.cn/test/authentication/check/'.$test_code;
        }
        $header = $this->getHeader($body, []);
        try {
            $response  = $client->request('POST', $uri, [
                'headers' => $header['header'],
                'json' => $header['encrypted_body'],
                'timeout' => 5
            ]);
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
            \Log::error("Anti Addiction - checkIdCard: ". $e->getMessage());
            return false;
        }

        $content = $response->getBody()->getContents();
        \Log::info("Anti Addiction - checkIdCard: ".$content);
        return json_decode($content, true);
    }

    /**
     * 实名认证结果查询
     * @param $ai
     */
    public function queryIdCard($ai, $test_code = '') {
        $client = new Client();
        $query_params = [
            'ai' => $ai
        ];
        $header = $this->getHeader([], $query_params);
        $uri = 'http://api2.wlc.nppa.gov.cn/idcard/authentication/query';
//        $uri = 'https://wlc.nppa.gov.cn/test/authentication/query';
        if($test_code) {
            $uri = 'https://wlc.nppa.gov.cn/test/authentication/query/'.$test_code;
        }
        try {
            $response  = $client->request('GET', $uri, [
                'headers' => $header['header'],
                'query' => $query_params,
                'timeout' => 5
            ]);
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
            \Log::error("Anti Addiction - queryIdCard: ". $e->getMessage());
            return false;
        }

        $content = $response->getBody()->getContents();
        \Log::info("Anti Addiction - queryIdCard: ".$content);

        return json_decode($content, true);
    }

    /**
     * 上报游戏用户上下线行为数据
     * @param $collections
     * @return mixed
     */
    public function reportLogInOut($collections, $test_code = '') {
        $client = new Client();

        $header = $this->getHeader(['collections' => $collections], []);
        $uri = 'http://api2.wlc.nppa.gov.cn/behavior/collection/loginout';
//        $uri = 'https://wlc.nppa.gov.cn/test/collection/loginout';
        if($test_code) {
            $uri = 'https://wlc.nppa.gov.cn/test/collection/loginout/'.$test_code;
        }
        try {
            $response  = $client->request('POST', $uri, [
                'headers' => $header['header'],
                'json' => $header['encrypted_body'],
                'timeout' => 5
            ]);
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
            \Log::error("Anti Addiction - reportLogInOut: ". $e->getMessage());
            return false;
        }

        $content = $response->getBody()->getContents();
        \Log::info("Anti Addiction - reportLogInOut: ".$content);
        return json_decode($content, true);
    }


    /**
     * 对请求报文体进行加密
     * @param $body
     * @return string
     */
    private function bodyEncrypt($body)
    {
        \Log::info("Anti Addiction - Encrypt Before:" .json_encode($body));
        $key = hex2bin($this->secret_key);
        $cipher = "aes-128-gcm";
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);

        $tag = null;
        $encrypt = openssl_encrypt(json_encode($body), $cipher, $key, OPENSSL_RAW_DATA,$iv,$tag);
        $str = bin2hex($iv) .  bin2hex($encrypt) . bin2hex($tag);
        $after = base64_encode(hex2bin($str));
        \Log::info("Anti Addiction - Encrypt After:" .$after);
        return $after;
    }

    /**
     * 接口签名
     * @param $body
     * @param $query_params
     */
    private function sign($body, $query_params, $timestamps)
    {
        if(!empty($body)) {
            $encrypted_body = json_encode(['data' => $this->bodyEncrypt($body)]);
        } else {
            $encrypted_body = '';
        }

        \Log::info("Anti Addiction - Encrpyted Body:".$encrypted_body);

        $sys_params = [
            'appId' => $this->app_id,
            'bizId' => $this->biz_id,
            'timestamps' => $timestamps
        ];
        $final_params = array_merge($sys_params, $query_params);
        ksort($final_params);

        $str_params = '';
        foreach ($final_params as $k => $v) {
            $str_params .= $k.$v;
        }
        $str_params .= $encrypted_body;
        $str_params = $this->secret_key.$str_params;

        \Log::info("Anti Addiction - Sign Before:".$str_params);
        $hash = hash('sha256', $str_params);

        \Log::info("Anti Addiction - Sign After:".$hash);
        return [
            'sign' => $hash,
            'encrypted_body' => json_decode($encrypted_body, true)
        ];
    }


    /**
     * 请求头构建
     * @param $body
     * @param $query_params
     * @return array
     */
    private function getHeader($body, $query_params) {
        list($msec, $sec) = explode(' ', microtime());
        $timestamps = sprintf('%d', (floatval($msec) + floatval($sec)) * 1000);

        $sign_data = $this->sign($body, $query_params, $timestamps);
        $header = [
            'Content-Type' => 'application/json;charset=utf-8',
            'appId' => $this->app_id,
            'bizId' => $this->biz_id,
            'timestamps' => $timestamps,
            'sign' => $sign_data['sign']
        ];



        \Log::info("Anti Addiction - Header:". json_encode($header));

        return [
            'header' => $header,
            'encrypted_body' => $sign_data['encrypted_body']
        ];

    }


}

2. 防沉迷系统接口测试 testcase01~08 命令

<?php

namespace App\Console\Commands;

use App\Services\AntiAddictionService;
use Illuminate\Console\Command;

class AntiAddictionTest extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'anti_addiction:test {--code= : 测试码} {--method= : 测试用例} {--bizId= : 游戏备案识别码}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '国家防沉迷系统接口测试';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    protected $service = null;

    public function __construct()
    {
        parent::__construct();

    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->service = new AntiAddictionService(env('ANTI_ADDICTION_APP_ID'), env('ANTI_ADDICTION_SECRET_KEY'), $this->option('bizId'));
        $method = $this->option('method');
        $this->$method();


    }

    // 实名认证接口 - 认证成功
    private function testcase01() {
        $this->service->checkIdCard('100000000000000001', '某一一', '110000190101010001', $this->option('code'));
    }

    // 实名认证接口 - 认证中
    private function testcase02() {
        $this->service->checkIdCard('200000000000000001', '某二一', '110000190201010009', $this->option('code'));
    }

    // 实名认证接口 - 认证失败
    private function testcase03() {
        $this->service->checkIdCard('300000000000000001', '某三一', '440682200001011111', $this->option('code'));
    }

    // 实名认证结果查询接口 - 认证成功
    private function testcase04() {
        $this->service->queryIdCard('100000000000000001', $this->option('code'));
    }

    // 实名认证结果查询接口 - 认证中
    private function testcase05() {
        $this->service->queryIdCard('200000000000000001', $this->option('code'));
    }

    // 实名认证结果查询接口 - 认证失败
    private function testcase06() {
        $this->service->queryIdCard('300000000000000001', $this->option('code'));
    }

    // 游戏用户行为数据上报接口 - 模拟“游客模式”下游戏用户行为数据上报场景
    private function testcase07() {
        $this->service->reportLogInOut([
            [
                'no' => 1,
                'si' => '888',
                'bt' => 0,
                'ot' => time(),
                'ct' => 2,
                'di' => '666',
            ]
        ], $this->option('code'));
    }

    // 游戏用户行为数据上报接口 - 模拟“已认证”下游戏用户行为数据上报场景
    private function testcase08() {
        $this->service->reportLogInOut([
            [
                'no' => 1,
                'si' => '888',
                'bt' => 0,
                'ot' => time(),
                'ct' => 0,
                'pi' => '1fffbjzos82bs9cnyj1dna7d6d29zg4esnh99u',
            ]
        ], $this->option('code'));
    }
}

欢迎加入QQ群:789235512 参与系统接入讨论与分享

本作品采用《CC 协议》,转载必须注明作者和本文链接
刘晓峰
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 3
Oraoto
$str = bin2hex($iv) .  bin2hex($encrypt) . bin2hex($tag);
$after = base64_encode(hex2bin($str));

为什么不直接 base64_encode($iv . $encrypt . $tag) :joy:

3年前 评论
全村的希望 (楼主) 3年前

遇到一个很坑的问题,上报接口 post 多条数据,直接收不到response

3年前 评论
changebeizhanyong 2年前
bfcaicai (作者) 2年前
changebeizhanyong 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!