接入AdMob的獎勵視頻廣告服務端的驗證,這裏指的用戶觀看完廣告發獎勵的通知驗證。進入谷歌的文檔說明
文檔使用的google自己的庫Tink,有必要的可以從倉庫找到這個庫進行使用。
先說兩個字段,這兩個是在您的廣告那邊設置的,item就是你發獎勵的商品id
reward_amount | 廣告單元設置中指定的獎勵金額。 | 5 |
reward_item | 廣告單元設置中指定的獎品。 | 金幣 |
轉到你的廣告可以設置
這個就是你前端定義的用戶信息
custom_data | 自定義數據字符串,其提供方法爲 setCustomData 。
如果應用未提供自定義數據字符串,此查詢參數值將不會出現在 SSV 回調中。 |
SAMPLE_CUSTOM_DATA_STRING |
再前端添加自定義的用戶數據
轉到您的廣告設置服務端驗證地址,谷歌是以get的方式請求過來的,比如您的地址是https://xxx/ads/callback
下面進入驗證的流程,設置好了地址,當用戶觀看完廣告就會通知你的服務器地址了。谷歌是SHA256withECDSA的驗證方式。
1、獲取 AdMob公鑰
https://www.gstatic.com/admob/reward/verifier-keys.json
從這個地址獲取公鑰,公鑰列表以 JSON 表示形式提供,我們根據回調參數的key_id尋找對應的公鑰,注意這裏返回的公鑰是base64編碼過的,使用的時候需要進行解碼
/**
* 獲取publicKey
* @param keyId
* @return
*/
public static String getVerifierKeys(final String keyId) {
// {"keys":[{"keyId":3335741209,"pem":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+nzvoGqvDeB9+SzE6igTl7TyK4JB\nbglwir9oTcQta8NuG26ZpZFxt+F2NDk7asTE6/2Yc8i1ATcGIqtuS5hv0Q==\n-----END PUBLIC KEY-----","base64":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+nzvoGqvDeB9+SzE6igTl7TyK4JBbglwir9oTcQta8NuG26ZpZFxt+F2NDk7asTE6/2Yc8i1ATcGIqtuS5hv0Q=="}]}
String publicKey=null;
String result=XHttp.get("https://www.gstatic.com/admob/reward/verifier-keys.json");
System.out.println("result:"+result);
JSONObject json=new JSONObject(result);
JSONArray jsonArray=json.getJSONArray("keys");
for(int i=0;i<jsonArray.length();i++) {
JSONObject jobj=jsonArray.getJSONObject(i);
String key=jobj.optString("keyId");
if(key.equalsIgnoreCase(keyId)) {
publicKey=jobj.getString("base64");
}
}
return publicKey;
}
返回的數據格式如下:
{
"keys": [
{
keyId: 1916455855,
pem: "-----BEGIN PUBLIC KEY-----\nMF...YTPcw==\n-----END PUBLIC KEY-----"
base64: "MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw=="
},
{
keyId: 3901585526,
pem: "-----BEGIN PUBLIC KEY-----\nMF...aDUsw==\n-----END PUBLIC KEY-----"
base64: "MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw=="
},
],
}
2、獲取要驗證的內容
public static Map<String,Object> getDataToVerify() {
String rewardUrl="http://xxx/ads/callback?ad_network=5450213213286189855&ad_unit=4826801875&custom_data=375759667%2C1%2CroleId&reward_amount=1&reward_item=Rewards×tamp=1590396116896&transaction_id=16855da7c17a63500096b5acca880b9d&signature=MEQCIAvEJKvCRd_7QLKEq6-BvsurUKpppJkQDFzBUe1ZqvO0AiBFRFEJAWAo0Qmaw2FbPDuC62cJD6XhMEPIfiyLkxK4ug&key_id=3335741209";
Map<String,Object> map=new HashMap<String, Object>();
System.out.println("rewardUrl:"+rewardUrl);
String SIGNATURE_PARAM_NAME = "signature=";
URI uri = null;
try {
uri = new URI(rewardUrl);
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("===11===");
}
String queryString = uri.getQuery();
int i = queryString.indexOf(SIGNATURE_PARAM_NAME);
if (i == -1) {
// throw new Exception("needs a signature query parameter");
System.out.println("===22===");
}
String ystr=queryString
.substring(0, i - 1);
System.out.println("yanz:"+ ystr);
byte[] queryParamContentData =
ystr
// i - 1 instead of i because of & in the query string
.getBytes(Charset.forName("UTF-8"));
String KEY_ID_PARAM_NAME = "key_id=";
String sigAndKeyId = queryString.substring(i);
i = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);
if (i == -1) {
System.out.println("needs a key_id query parameter");
}
String sig =
sigAndKeyId.substring(
SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);
String keyId = sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length());
System.out.println("sig:"+sig);
System.out.println("keyId:"+keyId);
map.put("queryParamContentData", queryParamContentData);
map.put("keyId", keyId);
try {
map.put("sig",org.apache.commons.codec.binary.Base64.decodeBase64(sig.getBytes("utf-8")) );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// map.put("sig", Base64.urlSafeDecode(sig));
return map;
}
rewardUrl就是您的回調地址和谷歌請求過來的參數,獲取驗證的內容queryParamContentData、keyId、sig
java獲取get請求參數和url的方式
StringBuffer url=request.getRequestURL();
url.append("?").append(request.getQueryString());
3、進行驗證
/**
* 驗證
* @param data 需要驗證的數據
* @param _key public key
* @param sig 谷歌返回的signature參數
* @return
*/
public static boolean verifySign(byte[] data, byte[] _key, byte[] sig) {
try {
java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
_key);
KeyFactory keyf = KeyFactory.getInstance("EC");
PublicKey publicKey = keyf.generatePublic(bobPubKeySpec);
Signature signer = Signature.getInstance("SHA256withECDSA");
signer.initVerify(publicKey);
signer.update(data);
return (signer.verify(sig));
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
return false;
}
}
Map<String,Object> map=getDataToVerify();
// ecdsaVerifyJce.verify((byte[])map.get("sig"), (byte[])map.get("queryParamContentData"));
String publicKey=getVerifierKeys((String)map.get("keyId"));
boolean isverfy=verifySign((byte[])map.get("queryParamContentData"), org.apache.commons.codec.binary.Base64.decodeBase64(publicKey),(byte[]) map.get("sig"));
System.out.println("isverfy:"+isverfy);
運行不發生異常,且返回true就是驗證通過了。
注意:獲取的公鑰base64的值需要進行base64解碼,回調過來的參數signature字段也是需要進行base64解碼