小7信息检测接入帮助
介绍

针对部分游戏本身不具备对敏感词的检测功能,提供给游戏的敏感词信息检测工具。

业务流程

1. 游戏厂商请求小7接口

2. 小7根据厂商请求需要检验的数判断是否需要拦截

3. 小7返回检测结果

4. 游戏厂商请求小7接口上报检测记录的处理方式

接口规范

接收请求的地址可以为统一的网关地址,也可以为每个接口配置单独的url;处理请求时需要根据传递的apiMethod区分不同的接口; 接口接入需要参照如下规范,所有接口的请求和响应均需要进行签名,字段统一为Request和Response表格中的定义, 业务请求参数bizParams和业务响应参数bizResp根据apiMethod有所不同。

请求方式

POST

请求格式

application/x-www-form-urlencoded

响应格式

JSON

请求字段定义

Request字段 类型 必选 说明
bizParams String 业务参数,值为每个接口对应业务参数的JSON字符串
apiMethod String 接口名称
reqTime String 请求时间,格式使用ISO8601规范,示例:2024-07-27T12:28:38+0800
appkey String 游戏appkey,如果双端使用相同appkey接入,osType字段必传
gameType String 游戏端类型,网游为client H5游戏为 h5
signature String 请求签名(签名方式参见下文)
osType String 系统类型,iosandroid

查看示例

//原始参数
object (size=7)
'bizParams' => string '{"roleId": "123", "guids": ["1234", "5678"], "guid": "", "serverId": ""}' (length=72)
'apiMethod' => string 'common.roleQuery' (length=16)
'reqTime' => string '2006-01-02T15:04:05+0800' (length=24)
'appkey' => string '0b9ce7b64b02fab5cd4ed3fas2as2ef6' (length=32)
'gameType' => string 'client' (length=6)
'signature' => string 'v371vuk4pv/TYGkmI0Mhm0U1a2Uk8aW/rc2dg3it9iywNUV0dcDdcbKew9ngq3zOWJ+tYP6EfK3F17LEO0LmLU4Phjs+wjDFOF0vj5OLWMUNNtF4jj/LZhpCxGBmyaBA91wb4ZaLNqqQYLHnEiptDy3aaYT+qL/nYQv/8yVunRo=' (length=172)
'osType' => string 'android' (length=7)

//查询字符串
string (length=396)
'bizParams={"roleId": "123", "guids": ["1234", "5678"], "guid": "", "serverId": ""}&apiMethod=common.roleQuery&reqTime=2006-01-02T15:04:05+0800&appkey=0b9ce7b64b02fab5cd4ed3fas2as2ef6&gameType=client&signature=v371vuk4pv/TYGkmI0Mhm0U1a2Uk8aW/rc2dg3it9iywNUV0dcDdcbKew9ngq3zOWJ+tYP6EfK3F17LEO0LmLU4Phjs+wjDFOF0vj5OLWMUNNtF4jj/LZhpCxGBmyaBA91wb4ZaLNqqQYLHnEiptDy3aaYT+qL/nYQv/8yVunRo=&osType=android'

//最终结果,urlencode后的查询字符串(发送请求时需要urlencode,生成签名时不需要)
string (length=480)
'bizParams=%7B%22roleId%22%3A+%22123%22%2C+%22guids%22%3A+%5B%221234%22%2C+%225678%22%5D%2C+%22guid%22%3A+%22%22%2C+%22serverId%22%3A+%22%22%7D&apiMethod=common.roleQuery&reqTime=2006-01-02T15%3A04%3A05%2B0800&appkey=0b9ce7b64b02fab5cd4ed3fas2as2ef6&gameType=client&signature=v371vuk4pv%2FTYGkmI0Mhm0U1a2Uk8aW%2Frc2dg3it9iywNUV0dcDdcbKew9ngq3zOWJ%2BtYP6EfK3F17LEO0LmLU4Phjs%2BwjDFOF0vj5OLWMUNNtF4jj%2FLZhpCxGBmyaBA91wb4ZaLNqqQYLHnEiptDy3aaYT%2BqL%2FnYQv%2F8yVunRo%3D&osType=android' 

响应字段定义

Response字段 类型 必选 说明
bizResp String 响应参数,值为每个接口对应响应参数的JSON字符串
apiMethod String 接口名称
respTime String 响应时间,格式使用ISO8601规范,示例:2024-07-27T12:28:38+0800
appkey String 游戏appkey
gameType String 游戏端类型,网游为client H5游戏为 h5
signature String 响应签名(签名方式参见下文)
osType String 系统类型,iosandroid

查看示例

{"bizResp":"{\"respCode\":\"SUCCESS\",\"respMsg\":\"\u67e5\u8be2\u6210\u529f\",\"role\":{\"roleId\":\"1\",\"guid\":\"1234\",\"roleName\":\"\u5c0f\u53ef\u7231\",\"serverId\":\"S1\",\"serverName\":\"1\u670d\",\"roleLevel\":\"100\",\"roleCE\":\"20000\",\"roleStage\":\"2-3\",\"roleRechargeAmount\":1314520},\"guidRoles\":[{\"roleId\":\"1\",\"guid\":\"1234\",\"roleName\":\"\u5c0f\u53ef\u7231\",\"serverId\":\"S1\",\"serverName\":\"1\u670d\",\"roleLevel\":\"100\",\"roleCE\":\"20000\",\"roleStage\":\"2-3\",\"roleRechargeAmount\":1314520},{\"roleId\":\"2\",\"guid\":\"1234\",\"roleName\":\"\u5c0f\u50bb\u74dc\",\"serverId\":\"S1\",\"serverName\":\"1\u670d\",\"roleLevel\":\"50\",\"roleCE\":\"10000\",\"roleStage\":\"1-3\",\"roleRechargeAmount\":520}]}","apiMethod":"common.roleQuery","respTime":"2006-01-02T15:04:05+0800","appkey":"0b9ce7b64b02fab5cd4ed3fas2as2ef6","gameType":"client","signature":"v371vuk4pv\/TYGkmI0Mhm0U1a2Uk8aW\/rc2dg3it9iywNUV0dcDdcbKew9ngq3zOWJ+tYP6EfK3F17LEO0LmLU4Phjs+wjDFOF0vj5OLWMUNNtF4jj\/LZhpCxGBmyaBA91wb4ZaLNqqQYLHnEiptDy3aaYT+qL\/nYQv\/8yVunRo=","osType":"android"}
接口概览
接口名称 apiMethod 请求方向 说明
信息检测 x7Detection.messageDetect 游戏Server => 小7Server 通过此接口可查询相关数据的检测结果
信息检测上报 x7Detection.messageDetectReport 游戏Server => 小7Server 通过此接口上报对检测文本的处理方式
签名与验签

1. 获取RSA秘钥对

签名验签过程需要两对RSA秘钥:

小7私钥 + 小7公钥:小7公钥在小7手游开放平台游戏管理>游戏详情页面获取。

游戏私钥 + 游戏公钥:游戏密钥对需要自行生成(可使用支付宝开放平台开发助手生成),公钥提交给小7对接人员进行配置,私钥妥善保管。

1. 发送请求给小7时(游戏Server => 小7Server):使用游戏私钥给请求参数签名,对小7接口返回的响应数据使用小7公钥进行验签。

2. 接收小7请求时(小7Server => 游戏Server):使用小7公钥对接收到的请求参数进行验签,响应请求时使用游戏私钥签名。

2. 签名验签规则及流程

① 请求与响应生成的签名的规则是不同的

对于请求方向为游戏Server => 小7Server的接口,发送请求给小7时,需要使用Request对应的参数与游戏私钥生成签名,接收到小7响应时,需要使用小7响应内容Response中的字段与小7公钥进行验签;

对于请求方向为小7Server => 游戏Server的接口,接收到小7请求时,需要使用小7请求体Request中对应的参数与小7公钥进行验签,对请求进行响应时,需要使用Response中的字段与游戏私钥生成签名;


② payload生成

发送请求(Request)生成签名所需的payload拼接生成方式如下:

POST + 空格 + $apiMethod + @ + $appkey + # + $gameType + . + $reqTime + \n\n + $bizParams

响应请求(Response)生成签名所需的payload拼接生成方式如下:

POST + 空格 + $apiMethod + @ + $appkey + # + $gameType + . + $respTime + \n\n + $bizResp

生成示例:

POST x7Detection.messageDetect@0b9ce7b64b02fb17cc948c0b9a6eb462#client.2024-07-27T12:28:38+0800{"checkMessage":"检测文本信息"}

③ 具体流程

a. 根据各接口定义获取业务参数bizParams或bizResp的json字符串,只需json_encode一次即可

b. 根据上述payload生成规则获取payload,注意请求和响应的payload是不一样的

c. 对请求签名时,将payload与游戏私钥使用openssl sha256算法进行运算生成原始签名,再将原始签名进行base64编码,得到最终signature的值

d. 验签时,将接收到signature进行base64解码,再将解码后的原始签名与payload一起使用小7公钥进行openssl sha256签名验证


④ 调试流程

签名验证不通过时,可参考以下流程排查问题:

a. 使用签名助手按照说明进行调试,确认可以调试通过;若不通过,请检查payload是否拼接正确,需要与签名助手结果窗口返回的payload一致。

b. 若是在响应小7请求时提示签名验证不通过,确认代码中使用的游戏私钥与在调试页面上填写的游戏公钥是否为一对秘钥;如果在调试页面未填写公钥进行调试,那么实际使用的游戏公钥为已提供给小7对接人员配置好的游戏公钥,可以与小7对接人员确认。

c. 若是对小7请求数据或响应数据进行签名验证不通过,确认代码中使用的小7公钥是否为开放平台对应游戏管理内页中提供的小7公钥。

3. Java代码参考

 
import org.apache.commons.codec.binary.Base64;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;


/**
 * RSA cipher util
 *
 */
public class RSA {

    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM_SHA256 = "SHA256withRSA";


    /**
     *
     * 用私钥对信息生成数字签名
     * 
     *
     * @param data 已加密数据
     * @param privateKey 私钥(BASE64编码)
     *
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA256);
        signature.initSign(privateK);
        signature.update(data);
        return Base64.encodeBase64String(signature.sign());
    }

    /**
     * 
     * 校验数字签名
     * 
     *
     * @param data 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign 数字签名
     *
     * @return
     * @throws Exception
     *
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        //SIGNATURE_ALGORITHM
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA256);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64.decodeBase64(sign));
    }

    //=== Testing ===
    public static void main(String[] args) {
        try {

            String payload = "POST x7Detection.messageDetect@0b9ce7b64b02fb17cc948c0b9a6eb462#client.2021-01-13T14:54:52+0800" +
                    "\n\n" +
                    "{\"detectionMessage\":\"检测文本信息\"}";

            //私钥和公钥字符串,可从签名助手页面下载测试秘钥
            String publicKey = "";
            String privateKey = "";
            //签名
            String sign = sign(payload.getBytes(), privateKey);

            //验签
            Boolean passed = verify(payload.getBytes(), publicKey, sign);

            System.out.println("payload=" + payload);
            System.out.println("---------------------");
            System.out.println("signature=" + sign);
            System.out.println("---------------------");


            System.out.println("passed ? " + (passed.equals(true)));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

4. PHP代码参考

<?php

//------------------------------------------------
// 签名加签函数PHP代码参考
//------------------------------------------------


class Signature
{

    /**
     * 签名
     *
     * @param string $payload
     * @param string $rsaPrivateKey
     * @param int $algo
     * @return string
     */
    public static function sign($payload, $rsaPrivateKey, $algo = OPENSSL_ALGO_SHA256)
    {
        $formatPrivateKey = self::formatRsaPrivateKey($rsaPrivateKey);
        openssl_sign($payload, $signature, $formatPrivateKey, $algo);
        return base64_encode($signature);
    }

    /**
     * 验签
     *
     * @param string $payload
     * @param string $signature
     * @param string $rsaPublicKey
     * @param int $algo
     * @return int
     */
    public static function verify($payload, $signature, $rsaPublicKey, $algo = OPENSSL_ALGO_SHA256)
    {
        $rawSignature = base64_decode($signature);
        $formatPublicKey = self::formatRsaPublicKey($rsaPublicKey);
        return openssl_verify($payload, $rawSignature, $formatPublicKey, $algo);
    }

    /**
     * 生成payload
     *
     * @param string $apiMethod
     * @param string $appkey
     * @param string $datetime
     * @param string $body
     * @param string $gameType
     * @param string $method
     * @return string
     */
    public static function genPayload($apiMethod, $appkey, $datetime, $body, $gameType, $method = "POST")
    {
        $payload = $method . " " . $apiMethod . "@" . $appkey . "#" . $gameType . "."
            . $datetime . "\n\n" . $body;
        return $payload;
    }


    /**
     * 格式化公钥
     *
     * @param string $publicKey
     * @return string
     */
    public static function formatRsaPublicKey($publicKey)
    {
        return "-----BEGIN PUBLIC KEY-----\r\n" . wordwrap($publicKey, 64, "\r\n", TRUE) . "\r\n-----END PUBLIC KEY-----";
    }

    /**
     * 格式化私钥
     *
     * @param string $privateKey
     * @return string
     */
    public static function formatRsaPrivateKey($privateKey)
    {
        return "-----BEGIN RSA PRIVATE KEY-----\r\n" . wordwrap($privateKey, 64, "\r\n", TRUE) . "\r\n-----END RSA PRIVATE KEY-----";
    }
}


$httpMethod = "POST";
$apiMethod = "x7Detection.messageDetect";
$appkey = "0b9ce7b64b02fb17cc948c0b9a6eb462";
$reqTime =  "2021-01-13T14:54:52+0800";
$gameType = "client";
$bizParams = '{"detectionMessage":"检测文本信息"}';


//RSA密钥对,可以从签名助手页面获取测试秘钥对
$rsaPrivateKey = "";
$rsaPublicKey = "";

//生成payload
$payload = Signature::genPayload($apiMethod, $appkey, $reqTime, $bizParams, $gameType);

//生成签名
$signature = Signature::sign($payload, $rsaPrivateKey);

//签名校验
$passed = Signature::verify($payload, $signature, $rsaPublicKey);

echo "--------------------------------------------------------------", PHP_EOL;
echo "payload=", $payload, PHP_EOL;
echo "--------------------------------------------------------------", PHP_EOL;
echo "signature=", $signature, PHP_EOL;
echo "--------------------------------------------------------------", PHP_EOL;
echo "passed=", $passed ? "true" : "false", PHP_EOL;
接口说明

接口apiMethod

x7Detection.messageDetect

请求方向

游戏Server => 小7Server

请求bizParams

bizParams字段 类型 必选 说明
guid String 角色所属小号ID
detectionMessage String 需要检测的信息

响应bizResp

bizResp字段 类型 必选 说明
respCode String 响应码,SUCCESS代表成功
respMsg String 响应提示信息
detectResult Array 道具信息数组
detectResult字段 类型 必选 说明
detectionLogId String 检查结果的ID
level String 检查结果的分类级别【1:通过,-1:不通过】
labelCode String 违规分类码【0:正常; 1:其他; 2:色情; 3:广告; 4:暴恐; 5:违禁; 6:涉政; 7:谩骂; 8:灌水; 9:违反广告法; 10:涉价值观】
sensitiveWords Array 敏感词数组

公共参数
双端使用相同appkey对接时,请正确选择系统类型
业务参数
调试说明

1. 填写以上公共参数和业务参数并提交

2. 参数设置成功提交后,测试接口地址一小时内返回的数据为配置的值

3. 请求测试调用接口地址(非正式地址):https://api.x7sy.com/x7DetectionHelper/gateway

4. 所有接口调试完毕,在上线之前需要切换为下面的正式接口地址

结果
接口说明

接口apiMethod

x7Detection.messageDetectReport

请求方向

游戏Server => 小7Server

请求bizParams

bizParams字段 类型 必选 说明
detectionLogId String 检查结果的ID
operateType String 对检测结果的处理类型【1:拦截发送(禁止该消息发送并提示发送文本中含有敏感信息); 2:不展示(允许发送但实际上会拦截不展示); 3、屏蔽关键词(屏蔽敏感词后剩余内容允许发送); 4、其他】

响应bizResp

bizResp字段 类型 必选 说明
respCode String 响应码,SUCCESS代表成功
respMsg String 响应提示信息

调试说明

1. 调用检测信息查询接口(x7Detection.messageDetect)后系统生成detectionLogId并返回,测试接口地址一小时内该ID可正常上报

2. 请求测试调用接口地址(非正式地址):https://api.x7sy.com/x7DetectionHelper/gateway

3. 所有接口调试完毕,在上线之前需要切换为下面的正式接口地址

签名调试
调试说明

1. 签名助手使用测试RSA密钥对,私钥下载公钥下载,此密钥对仅供此页面测试使用,勿做其他用途

2. 自行拼接payload,并使用测试私钥生成签名后,将签名及签名payload使用的参数填写至上方表单中

3. 提交表单之后可自动验证签名是否计算正确

4. 实际请求接口时,注意请求内容格式为application/x-www-form-urlencoded(形如key=value&key=value...),对value需要进行urlencode(在生成签名之后)

结果

签名的RSA秘钥如何获取与使用,它们有什么区别?

签名验签过程需要两对RSA秘钥:

小7私钥 + 小7公钥:小7公钥在小7手游开放平台游戏管理>游戏详情页面获取。

游戏私钥 + 游戏公钥:游戏密钥对需要自行生成(可使用支付宝开放平台开发助手生成),公钥提交给小7对接人员进行配置,私钥妥善保管。

1. 发送请求给小7时(游戏Server => 小7Server):使用游戏私钥给请求参数签名,对小7接口返回的响应数据使用小7公钥进行验签。

2. 接收小7请求时(小7Server => 游戏Server):使用小7公钥对接收到的请求参数进行验签,响应请求时使用游戏私钥签名。


签名助手可以对响应数据进行签名校验吗?
可以。签名助手既可以对Request参数签名进行校验,也可以对Response参数签名进行校验;在填写响应参数进行校验时,respTime可以填写至reqTime字段中,bizResp可以填写至bizParams字段中。

签名助手调试签名通过,但是在接口调试助手发起请求时签名不通过?

1. 签名助手使用rsa秘钥对为测试密钥对,实际请求时不可使用测试密钥对

2. 如果在调试助手调试时填写了游戏公钥,则需要确认代码中用来生成签名的游戏私钥与填写的游戏公钥为匹配的密钥对

3. 签名助手填写信息时不需要urlencode,实际发送请求时需要对Request参数进行urlencode


有接入SDK可以使用吗?
目前提供了Java和PHP版本的小7商城接入SDK,接入小7商城可以直接使用,接入其他接口也可以参考SDK相关代码进行开发,点击前往下载SDK


检测信息查询与处理方式上报两个接口都要接入吗?
是的。调用检测接口获得检测结果,对检测结果进行处理后再上报处理方式。

为什么请求检测接口参数传递了敏感词但是接口返回了检测通过?
请检查是否使用的是正式检测接口https://api.x7sy.com/x7Detection/gateway

请求检测接口返回检测不通过怎么处理?
小7不要求具体处理方式,由游戏方自主决定并上报。

上报处理类型应该上报哪种?
根据实际处理方式选择对应的operateType上报,类型枚举见接口文档。