快游戲如何校驗登錄簽名


需求描述

快游戲開發者調用帳號登錄接口成功后,會收到 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