快游戏如何校验登录签名


需求描述

快游戏开发者调用帐号登录接口成功后,会收到 gameAuthSign 字段,代表华为服务器返回的登录签名,建议开发者调用“校验登录签名接口”对返回的签名进行校验,以此确保登录结果的准确性。

H5 快游戏:

Runtime 快游戏:

需求分析

校验登录签名的接口文档如下:

https://developer.huawei.com/consumer/cn/doc/development/HMS-2-References/hmssdk_jointOper_api_reference_s4

解决方案

服务端 java 代码如下所示

Loginmain.java(入口文件,传入参与签名校验的参数)

/**
  * 本程序可以用于应用服务端发送验证登录签名请求时,使用真实数据测试验签结果。
  */
 package com.huawei.test;
 import java.util.HashMap;
 import java.util.Map;
 public class Loginmain {
     public static void main(String[] args)
     {
         // CP签名游戏私钥
         final String CP_AUTH_SIGN_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALQ8JRusTb3swpvmeOXbVjuJmf3e8PMbwhr5670hI8IgrYykszXBNOXCSaysr6m6MFw8zRG4BPDxFmauBYMVWZKpcce/lKfmw8s9DwYXQ+weu ==";
         // 游戏服务器提供给CP签名接口URL
         final String requestUri = "https://jos-api.cloud.huawei.com/gameservice/api/gbClientApi";
         // CP请求参数
         Map<String,String> mockRequestParams = new HashMap<>();
         mockRequestParams.put("method","external.hms.gs.checkPlayerSign");
         mockRequestParams.put("appId","103573859");
         mockRequestParams.put("cpId","900086000034703063");
         mockRequestParams.put("ts","1609388135341");
         mockRequestParams.put("playerId","1180392374801211");
         mockRequestParams.put("playerLevel","1");
         mockRequestParams.put("playerSSign","NZWHB7q4cgAvX2L2E9ZCdDnUkUJYrswOFNVAjHMKgMO8SgFRxderjzpRqDrYZv6n0UuJC4MYG1fBCPjW8%2FKJa4%2Bm90j12%2FiyEZz7VwXkIi6Azgk6ILdRW%2BH4lNjINl3th7PN7qo%2F0C%2BNWOFuPXRoCcntm%2FYWOG0Ak1a0keSdLeqRZZ0w9OyyQMkzRKVllL1hOEpVia3CwpEzHnrGe8IrV%2Bchr8ERj4I9oYvU9PTjyyCv%2FTWJ7eF0Xe5Ws3JfdBeR8R1dINj%2Fr%2BSaPViVDBY%2BMvduDGTjMyD5dPRN4QaK%2FYtOFqNPscW%2B0%2FCq18wSsnpihdbC5TmTrDEylThZf5Yt2Q%3D%3D");
         GameServiceCallDemo.callGameService(requestUri,mockRequestParams,CP_AUTH_SIGN_PRIVATE_KEY);
     }
 }

GameServiceCallDemo.java(用于调用签名校验接口进行校验)

package com.huawei.test;
 import com.alibaba.fastjson.JSON;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.conn.ssl.NoopHostnameVerifier;
 import org.apache.http.conn.ssl.TrustStrategy;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.ssl.SSLContexts;
 import org.apache.http.util.Asserts;
 import org.apache.http.util.EntityUtils;
 
 import javax.net.ssl.SSLContext;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.nio.charset.Charset;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.Signature;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.*;
 public class GameServiceCallDemo
 {
     private static final String RETURN_CODE_SUCCEED = "0";
     private static final int HTTP_RESPONSE_STATUS_CODE_OK = 200;
     /**
      *
      * @param requestUri 请求URL
      * @param requestParams 请求参数对
      * @param cpAuthKey CP侧签名私钥
      */
     public static void callGameService(String requestUri, Map<String,String> requestParams, final String cpAuthKey)
     {
         requestParams.put("cpSign",generateCPSign(requestParams,cpAuthKey));
         // 响应消息中返回参数
         Map<String,Object> responseParamPairs = doPost(requestUri,requestParams);
         if(responseParamPairs.isEmpty())
         {
             System.out.println("None response parameter.");
         }
         else
         {
             if(RETURN_CODE_SUCCEED.equalsIgnoreCase(getString("rtnCode",responseParamPairs)))
             {
                 System.out.println("rtnCode: " + getString("rtnCode",responseParamPairs));
                 System.out.println("ts: " + getString("ts",responseParamPairs));
                 System.out.println("rtnSign: " + getString("rtnSign",responseParamPairs));
             }
             else
             {
                 System.out.println("rtnCode: " + getString("rtnCode",responseParamPairs));
                 System.out.println("errMsg: " + getString("errMsg",responseParamPairs));
             }
         }
     }
     private static String getString(String key,Map<String,Object> responseParamPairs)
     {
         Asserts.notNull(responseParamPairs, "responseParamPairs");
         Object value = responseParamPairs.get(key);
         if (value == null) {
             return null;
         }
         return value.toString();
     }
     /**
      *     创建跳过SSL验证的httpClient实例,https://jos-api.cloud.huawei.com/gameservice/api/gbClientApi这个地址暂时没有添加SSL证书,所以需要跳过SSL验证
      */
     private static CloseableHttpClient getIgnoeSSLClient() throws Exception {
         SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
             @Override
             public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                 return true;
         }
     }).build();
     //创建httpClient
     CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).
             setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
         return client;
 }
     private static Map<String,Object> doPost(String url, Map<String, String> paramaters)
     {
         HttpPost httpReq = new HttpPost(url);
         // 创建无需SSL验证的httpClient实例.
         CloseableHttpClient httpclient = null ;
         try {
             httpclient = getIgnoeSSLClient();
         } catch (Exception e1) {
             // TODO Auto-generated catch block
             e1.printStackTrace();
         }
         try
         {
             if (paramaters != null)
             {
                 List<NameValuePair> paramPairs = new ArrayList<NameValuePair>();
                 BasicNameValuePair bnv;
                 for (Map.Entry<String, String> entry : paramaters.entrySet())
                 {
                     bnv = new BasicNameValuePair(entry.getKey(), entry.getValue());
                     paramPairs.add(bnv);
                 }
                 httpReq.setEntity(new UrlEncodedFormEntity(paramPairs, "UTF-8"));
             }
             Map<String,Object> responseParamPairs = new HashMap<>();
             HttpResponse resp = httpclient.execute(httpReq);
             if(null != resp && HTTP_RESPONSE_STATUS_CODE_OK == resp.getStatusLine().getStatusCode())
             {
                 responseParamPairs = JSON.parseObject(EntityUtils.toString(resp.getEntity()));
             }
             return responseParamPairs;
         }
         catch (Exception e)
         {
             e.printStackTrace();
             return null;
         }
         finally
         {
             try
             {
                 httpclient.close();
             }
             catch (IOException e)
             {
                 e.printStackTrace();
             }
         }
     }
     private static String sign(byte[] data, String privateKey)
     {
         try
         {
             byte[] e = Base64.decodeBase64(privateKey);
             PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(e);
             KeyFactory keyFactory = KeyFactory.getInstance("RSA");
             PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
             Signature signature = Signature.getInstance("SHA256WithRSA");
             signature.initSign(privateK);
             signature.update(data);
             String signs = Base64.encodeBase64String(signature.sign());
             return signs;
         }
         catch (Exception var)
         {
             System.out.println("SignUtil.sign error." + var);
             return "";
         }
     }
     /**
      * 根据参数Map构造排序好的参数串
      *
      * @param params
      * @return
      */
     private static String format(Map<String, String> params)
     {
         StringBuffer base = new StringBuffer();
         Map<String, String> tempMap = new TreeMap<String, String>(params);
         // 获取计算nsp_key的基础串
         try
         {
             for (Map.Entry<String, String> entry : tempMap.entrySet())
             {
                 String k = entry.getKey();
                 String v = entry.getValue();
                 base.append(k).append("=").append(URLEncoder.encode(v, "UTF-8")).append("&");
             }
         }
         catch (UnsupportedEncodingException e)
         {
             System.out.println("Encode parameters failed.");
             e.printStackTrace();
         }
         String body = base.toString().substring(0, base.toString().length() - 1);
         // 空格和星号转义
         body = body.replaceAll("\\+", "%20").replaceAll("\\*", "%2A");
         return body;
     }
     private static String generateCPSign(Map<String,String> requestParams,final String cpAuthKey)
     {
         // 对消息体中查询字符串按字典序排序并且进行URLCode编码
         String baseStr = format(requestParams);
         // 用CP侧签名私钥对上述编码后的请求字符串进行签名
         String cpSign = sign(baseStr.getBytes(Charset.forName("UTF-8")), cpAuthKey);
         return cpSign;
     }
 }

欲了解更多详情,请参见:

H5 快游戏登录介绍:

https://developer.huawei.com/consumer/cn/doc/development/quickApp-Guides/quickgame-develop-h5-game#h2-1580716161397

runtime 快游戏登录介绍:

https://developer.huawei.com/consumer/cn/doc/development/quickApp-References/quickgame-api-account#gameLogin


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM