[国家新闻出版署]网络游戏防沉迷实名认证系统接口对接实例(Java版)

本文主要是介绍[国家新闻出版署]网络游戏防沉迷实名认证系统接口对接实例(Java版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        最近又有游戏要对接网络游戏防沉迷实名认证系统接口,又又搞了我三天两夜才把接口对接完毕,确实难受的一批.其实之前对接过,无奈没有保留代码,导致痛苦的事情又经历一遍,这次总结经验,于是有了这篇文章.

        首先记录下(备忘)官方网站地址:网络游戏防沉迷实名认证系统 (nppa.gov.cn)

        接着上正戏,因为本人从事的是Java,所有只有Java的实现,不是同行可以先走一步了,本人得意声明:所有代码都是我自己写的,如有雷同不胜荣幸!

        1.先看看我的代码目录结构

接着就是每一个类的内容展示(顺序从上至下):

2.接口回调的所有编码,我做成枚举类

package com.xxx.xxx.api.authentication.enumData;public enum AuthResponseCode {OK(0, "OK", "请求成功"),SYS_ERROR(1001, "SYS ERROR", "系统错误"),SYS_REQ_RESOURCE_NOT_EXIST(1002, "SYS REQ RESOURCE NOT EXIST", "接口请求的资源不存在"),SYS_REQ_METHOD_ERROR(1003, "SYS REQ METHOD ERROR", "接口请求方式错误"),SYS_REQ_HEADER_MISS_ERROR(1004, "SYS REQ HEADER MISS ERROR", "接口请求核心参数缺失"),SYS_REQ_IP_ERROR(1005, "SYS REQ IP ERROR", "接口请求IP地址非法"),SYS_REQ_BUSY_ERROR(1006, "SYS REQ BUSY ERROR", "接口请求超出流量限制"),SYS_REQ_EXPIRE_ERROR(1007, "SYS REQ EXPIRE ERROR", "接口请求过期"),SYS_REQ_PARTNER_ERROR(1008, "SYS REQ PARTNER ERROR", "接口请求方身份非法"),SYS_REQ_PARTNER_AUTH_DISABLE(1009, "SYS REQ PARTNER AUTH DISABLE", "接口请求方权限未启用"),SYS_REQ_AUTH_ERROR(1010, "SYS REQ AUTH ERROR", "接口请求方无该接口权限"),SYS_REQ_PARTNER_AUTH_ERROR(1011, "SYS REQ PARTNER AUTH ERROR", "接口请求方身份核验错误"),SYS_REQ_PARAM_CHECK_ERROR(1012, "SYS REQ PARAM CHECK ERROR", "接口请求报文核验失败");private final int code;private final String message;private final String description;AuthResponseCode(int code, String message, String description) {this.code = code;this.message = message;this.description = description;}public int getCode() {return code;}public String getMessage() {return message;}public String getDescription() {return description;}public static AuthResponseCode fromCode(int code) {for (AuthResponseCode responseCode : AuthResponseCode.values()) {if (responseCode.getCode() == code) {return responseCode;}}throw new IllegalArgumentException("Unknown error code: " + code);}
}

3.实体DO类

package com.xxx.xxx.api.authentication.vo;import lombok.Data;@Data
public class AuthResponse<T> {private Integer errcode;private String errmsg;private T data;}
package com.xxx.xxx.api.authentication.vo;public class AuthResponseData {private CheckQueryResult result;public CheckQueryResult getResult() {return result;}public void setResult(CheckQueryResult result) {this.result = result;}public static class CheckQueryResult {private int status;private String pi;public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}public String getPi() {return pi;}public void setPi(String pi) {this.pi = pi;}}
}
package com.xxx.xxx.api.authentication.vo;import com.alibaba.fastjson.annotation.JSONType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@JSONType(orders = {"no", "si", "bt", "ot", "ct", "di", "pi"})
public class LoginoutDO {private Integer no;       // 条目编码private String si;        // 游戏内部会话标识private Integer bt;       // 用户行为类型private Long ot;          // 行为发生时间戳,单位秒private Integer ct;       // 上报类型private String di;        // 设备标识(游客模式下必填)private String pi;        // 用户唯一标识(已认证通过用户必填)/*** 枚举类,表示用户行为类型*/public enum BehaviorType {/*** 下线*/OFFLINE(0),/*** 上线*/ONLINE(1);private final int value;BehaviorType(int value) {this.value = value;}public int getValue() {return value;}}/*** 枚举类,表示用户行为数据上报类型*/public enum ReportType {/*** 已认证通过用户*/CERTIFIED_USER(0),/*** 游客用户*/GUEST_USER(2);private final int value;ReportType(int value) {this.value = value;}public int getValue() {return value;}}
}

代码都很简单,我就不一一阐述了,下面有请主菜登场(此处应有掌声👏🏻~)

 4.主角登场

package com.xxx.xxx.api.authentication;import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Maps;
import com.xxx.xxx.api.authentication.vo.AuthResponse;
import com.xxx.xxx.api.authentication.vo.AuthResponseData;
import com.xxx.xxx.api.authentication.vo.LoginoutDO;
import com.xxx.xxx.exception.AuthenticationRunTimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;import static java.nio.charset.StandardCharsets.UTF_8;/*** 游戏防沉迷系统接口*/
@Component
@Slf4j
public class GameIdCardAuth {private static final String CHECK_URL = "https://api.wlc.nppa.gov.cn/idcard/authentication/check";private static final String QUERY_URL = "http://api2.wlc.nppa.gov.cn/idcard/authentication/query";private static final String LOGINOUT_URL = "http://api2.wlc.nppa.gov.cn/behavior/collection/loginout";@Value("${lib.IdCardAuth.secret_key}")private String SECRET_KEY;@Value("${lib.IdCardAuth.app_id}")private String APP_ID;@Value("${lib.IdCardAuth.biz_id}")private String BIZ_ID; // 游戏备案码@Autowiredprivate RestTemplate restTemplate;/*** 1.实名认证接口(游戏运营单位调用该接口进行用户实名认证工作,本版本仅支持大陆地区的姓名和二代身份证号核实认证)* @param ai 你猜啊* @param name 身份证名字* @param idNum 身份证号码*/public AuthResponse<AuthResponseData> doPostCheck(String ai, String name, String idNum) {long nowTime = System.currentTimeMillis();Map<String, Object> paramMap = Maps.newHashMap();paramMap.put("ai", ai);paramMap.put("name", name);paramMap.put("idNum", idNum);String content = JSONObject.toJSONString(paramMap);byte[] keyBytes = hexStringToByte(SECRET_KEY);String encryptStr = aesGcmEncrypt(content, keyBytes);log.debug("加密后的内容: {}", encryptStr);Map<String, String> signMap = createSignMap(nowTime);String sign;try {sign = generateCheckSignature(signMap, "{\"data\":\"" + encryptStr + "\"}");log.debug("签名结果:" + sign);} catch (NoSuchAlgorithmException e) {throw new AuthenticationRunTimeException("防沉迷doPostCheck签名异常:{}", e);}HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.add("appId", signMap.get("appId"));headers.add("bizId", signMap.get("bizId"));headers.add("timestamps", signMap.get("timestamps"));headers.add("sign", sign);HttpEntity<String> request = new HttpEntity<>("{\"data\":\"" + encryptStr + "\"}", headers);ResponseEntity<String> response = restTemplate.postForEntity(CHECK_URL, request, String.class);log.debug("响应内容: {}", response.getBody());// 将响应数据转换为封装类return JSONObject.parseObject(response.getBody(), new TypeReference<AuthResponse<AuthResponseData>>() {});}/*** 2.实名认证结果查询接口(追加查询未验证的玩家)* @param ai 继续猜啊*/public AuthResponse<AuthResponseData> doGetQuery(String ai) {long nowTime = System.currentTimeMillis();String params = "ai=" + ai;Map<String, String> signMap = createSignMap(nowTime);String sign = null;try {sign = generateQuerySignature(signMap, "ai" + ai);log.debug("签名结果:" + sign);} catch (NoSuchAlgorithmException e) {throw new AuthenticationRunTimeException("防沉迷doGetQuery签名异常:{}", e);}HttpHeaders headers = new HttpHeaders();headers.add("appId", signMap.get("appId"));headers.add("bizId", signMap.get("bizId"));headers.add("timestamps", signMap.get("timestamps"));headers.add("sign", sign);HttpEntity<String> entity = new HttpEntity<>(headers);ResponseEntity<String> response = restTemplate.exchange(QUERY_URL + "?" + params, HttpMethod.GET, entity, String.class);log.debug("响应内容: {}", response.getBody());// 将响应数据转换为封装类return JSONObject.parseObject(response.getBody(), new TypeReference<AuthResponse<AuthResponseData>>() {});}/*** 3.游戏用户行为数据上报接口(上报游戏用户上下线行为数据)* @param loginoutDOList 玩家行为*/public AuthResponse<String> doPostLoginout(List<LoginoutDO> loginoutDOList) {long nowTime = System.currentTimeMillis();Map<String, Object> paramMap = Maps.newHashMap();paramMap.put("collections", loginoutDOList);String content = JSONObject.toJSONString(paramMap);byte[] keyBytes = hexStringToByte(SECRET_KEY);String encryptStr = aesGcmEncrypt(content, keyBytes);log.debug("加密后的内容: {}", encryptStr);Map<String, String> signMap = createSignMap(nowTime);String sign;try {sign = generateLoginoutSignature(signMap, "{\"data\":\"" + encryptStr + "\"}");log.debug("签名结果:" + sign);} catch (NoSuchAlgorithmException e) {throw new AuthenticationRunTimeException("防沉迷loginoutDOList签名异常:{}", e);}HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.add("appId", signMap.get("appId"));headers.add("bizId", signMap.get("bizId"));headers.add("timestamps", signMap.get("timestamps"));headers.add("sign", sign);HttpEntity<String> request = new HttpEntity<>("{\"data\":\"" + encryptStr + "\"}", headers);ResponseEntity<String> response = restTemplate.postForEntity(LOGINOUT_URL, request, String.class);log.debug("响应内容: {}", response.getBody());// 将响应数据转换为封装类return JSONObject.parseObject(response.getBody(), new TypeReference<AuthResponse<String>>() {});}private String generateCheckSignature(Map<String, String> signMap, String content) throws NoSuchAlgorithmException {StringBuilder sb = new StringBuilder();sb.append(SECRET_KEY);for (Map.Entry<String, String> entry : signMap.entrySet()) {sb.append(entry.getKey()).append(entry.getValue());}sb.append(content);log.debug("待签名字符串:" + sb);return sign(sb.toString());}private String generateQuerySignature(Map<String, String> signMap, String content) throws NoSuchAlgorithmException {StringBuilder sb = new StringBuilder();sb.append(SECRET_KEY);sb.append(content);for (Map.Entry<String, String> entry : signMap.entrySet()) {sb.append(entry.getKey()).append(entry.getValue());}log.debug("待签名字符串:" + sb);return sign(sb.toString());}private String generateLoginoutSignature(Map<String, String> signMap, String content) throws NoSuchAlgorithmException {StringBuilder sb = new StringBuilder();sb.append(SECRET_KEY);for (Map.Entry<String, String> entry : signMap.entrySet()) {sb.append(entry.getKey()).append(entry.getValue());}sb.append(content);log.debug("待签名字符串:" + sb);return sign(sb.toString());}private Map<String, String> createSignMap(long nowTime) {Map<String, String> signMap = new TreeMap<>();signMap.put("appId", APP_ID);signMap.put("bizId", BIZ_ID);signMap.put("timestamps", String.valueOf(nowTime));return signMap;}private String sign(String toBeSignStr) throws NoSuchAlgorithmException {MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");messageDigest.update(toBeSignStr.getBytes(UTF_8));return byteToHexString(messageDigest.digest());}private static String aesGcmEncrypt(String content, byte[] key) {try {Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");SecretKeySpec skey = new SecretKeySpec(key, "AES");cipher.init(Cipher.ENCRYPT_MODE, skey);byte[] ivb = cipher.getIV();byte[] encodedByteArray = cipher.doFinal(content.getBytes(UTF_8));byte[] message = new byte[ivb.length + encodedByteArray.length];System.arraycopy(ivb, 0, message, 0, ivb.length);System.arraycopy(encodedByteArray, 0, message, ivb.length, encodedByteArray.length);return java.util.Base64.getEncoder().encodeToString(message);} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |BadPaddingException e) {return null;}}private static String aesGcmDecrypt(String content, byte[] key) {try {Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");SecretKeySpec skey = new SecretKeySpec(key, "AES");byte[] encodedArrayWithIv = java.util.Base64.getDecoder().decode(content);GCMParameterSpec decryptSpec = new GCMParameterSpec(128, encodedArrayWithIv, 0, 12);decryptCipher.init(Cipher.DECRYPT_MODE, skey, decryptSpec);byte[] b = decryptCipher.doFinal(encodedArrayWithIv, 12, encodedArrayWithIv.length - 12);return new String(b, UTF_8);} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |BadPaddingException | InvalidAlgorithmParameterException e) {return null;}}private static byte[] hexStringToByte(String str) {if (str == null || str.equals("")) {return null;}str = str.toUpperCase();int length = str.length() / 2;char[] hexChars = str.toCharArray();byte[] d = new byte[length];for (int i = 0; i < length; i++) {int pos = i * 2;d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));}return d;}private static byte charToByte(char c) {return (byte) "0123456789ABCDEF".indexOf(c);}private static String byteToHexString(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {int hex = b & 0xff;String hexStr = Integer.toHexString(hex);if (hexStr.length() == 1) {hexStr = '0' + hexStr;}sb.append(hexStr);}return sb.toString();}
}

这里着重讲一下吧,其实接口有四个,有一个玩家消费的接口,在官方文档介绍有提过,但是在接口文档却只有这三个接口,所有就先实现三个吧.

下面就看简单的测试实例:

package com.xxx.xxx;import com.zhcj.gums.api.authentication.GameIdCardAuth;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {GumsApplication.class, UnitTest.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UnitTest {// @MockBean// private NettyServer nettyServer;//排除启动@Autowiredprivate GameIdCardAuth gameIdCardAuth;/*** 防沉迷接口测试*/@Testpublic void authentication() {System.out.println("实名认证接口");System.out.println(gameIdCardAuth.doPostCheck("100000000000000001", "某一一", "110000190101010001"));// System.out.println("实名认证结果查询接口");// System.out.println(gameIdCardAuth.doGetQuery("100000000000000001"));// System.out.println("实名认证接口");// LoginoutDO loginoutDO = new LoginoutDO();// loginoutDO.setNo(1)//         .setSi("1fffbl6st3fbp199i8zh5ggcp84fgo3rj7pn1y")//         .setBt(LoginoutDO.BehaviorType.ONLINE.getValue())//         .setOt(System.currentTimeMillis() / 1000)//         .setCt(LoginoutDO.ReportType.CERTIFIED_USER.getValue())//         .setDi("1fffbl6st3fbp199i8zh5ggcp84fgo3rj7pn1y")//         .setPi("1fffbl6st3fbp199i8zh5ggcp84fgo3rj7pn1y");// System.out.println(gameIdCardAuth.doPostLoginout(Lists.newArrayList(loginoutDO)));}
}

嗯,主打一个有手就行~

这篇关于[国家新闻出版署]网络游戏防沉迷实名认证系统接口对接实例(Java版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1125993

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,