伺服器端驗證回呼是包含查詢參數的網址要求 方法是 Google 將資料擴展至外部系統 提醒使用者該與獎勵廣告互動或 插頁式獎勵廣告。獎勵 SSV (伺服器端驗證) 回呼 額外增添一層防護,防止用戶端回呼遭到假冒 來獎勵使用者
本指南將說明如何使用 Tink Java 應用程式第三方 來確保回呼中的查詢參數 正當價值 雖然本指南會將 Tink 用於本指南,但您還是可以選擇 採用 ECDSA: 您也可以使用測試工具 工具。
請造訪這個網頁 示例 使用 Java Spring-boot。
必要條件
使用 Tink Java 應用程式程式庫中的 獎勵 AdsVerifier
Tink Java Apps GitHub 存放區
包含
RewardedAdsVerifier
敬上
輔助類別,減少驗證獎勵 SSV 回呼所需的程式碼。
使用此類別時,您可以使用下列程式碼驗證回呼網址。
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
如果 verify()
方法在未引發例外狀況的情況下執行,回呼
已成功驗證網址。獎勵使用者
。換
此類別為了驗證獎勵 SSV 回呼而執行的步驟細目。
您可以詳閱手動驗證獎勵廣告
SSV 部分。
SSV 回呼參數
伺服器端驗證回呼包含查詢參數,用來說明 獎勵廣告互動。參數名稱、說明和範例值如下: 。參數會依字母順序傳送。
參數名稱 | 說明 | 範例值 |
---|---|---|
ad_network | 提供這則廣告的廣告來源廣告來源 ID。廣告來源 與編號值對應的名稱會列在《 來源 ID 區段, | 1953547073528090325 |
ad_unit | 用來請求獎勵廣告的 AdMob 廣告單元 ID。 | 2747237135 |
key_id | 用於驗證 SSV 回呼的金鑰。這個值對應至公開金鑰 由 AdMob 金鑰伺服器提供的圖示組成 | 1234567890 |
reward_amount | 廣告單元設定中指定的獎勵金額。 | 5 |
reward_item | 廣告單元設定中指定的獎勵項目。 | 硬幣 |
簽章 | AdMob 產生的 SSV 回呼簽名。 | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
timestamp | 使用者獲得獎勵的時間戳記,以毫秒為單位。 | 1507770365237823 |
transaction_id | 由 AdMob 產生的每個獎勵公益活動的專屬十六進位編碼 ID。 | 18fa792de1bca816048293fc71035638 |
user_id | 使用者提供的使用者 ID
SetUserId 。
如果應用程式未提供使用者 ID,這個查詢參數就不會 都會出現在 SSV 回呼中。 |
1234567 |
廣告來源 ID
廣告來源名稱和 ID
广告来源名称 | 广告来源 ID |
---|---|
Aarki(出价) | 5240798063227064260 |
Ad Generation(出价) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony(非 SDK)(出价) | 4600416542059544716 |
AdColony(出价) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
AdMob 广告联盟 | 5450213213286189855 |
AdMob 广告联盟广告瀑布流 | 1215381445328257950 |
ADResult | 10593873382626181482 |
AMoAd | 17253994435944008978 |
AppLovin | 1063618907739174004 |
AppLovin(出价) | 1328079684332308356 |
Chartboost | 2873236629771172317 |
Chocolate Platform(出价) | 6432849193975106527 |
跨渠道 (MdotM) | 9372067028804390441 |
自定义事件 | 18351550913290782395 |
DT Exchange* * 在 2022 年 9 月 21 日之前,该广告联盟称为“Fyber Marketplace”。 | 2179455223494392917 |
EMX(出价) | 8497809869790333482 |
Fluct(出价) | 8419777862490735710 |
小风 | 3376427960656545613 |
Fyber* * 此广告来源用于生成历史报告。 | 4839637394546996422 |
i-mobile | 5208827440166355534 |
优化数字化(出价) | 159382223051638006 |
Index Exchange(出价) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi(出价) | 6325663098072678541 |
InMobi Exchange(出价) | 5264320421916134407 |
IronSource | 6925240245545091930 |
ironSource Ads(出价) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
LG U+AD | 18298738678491729107 |
LINE 广告联盟 | 3025503711505004547 |
maio | 7505118203095108657 |
maio(出价) | 1343336733822567166 |
Media.net(出价) | 2127936450554446159 |
参与中介的自家广告 | 6060308706800320801 |
Meta Audience Network* * 在 2022 年 6 月 6 日之前,该广告联盟称为“Facebook Audience Network”。 | 10568273599589928883 |
Meta Audience Network(出价)* * 在 2022 年 6 月 6 日之前,该广告联盟称为“Facebook Audience Network(出价)”。 | 11198165126854996598 |
Mintegral | 1357746574408896200 |
Mintegral(出价) | 6250601289653372374 |
MobFox | 8079529624516381459 |
MobFox(出价) | 3086513548163922365 |
MoPub(已弃用) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen(出价)* * 在 2024 年 5 月 1 日之前,该广告联盟称为“UnrulyX”。 | 2831998725945605450 |
ONE by AOL (Millennial Media) | 6101072188699264581 |
ONE by AOL (Nexage) | 3224789793037044399 |
OneTag Exchange(出价) | 4873891452523427499 |
OpenX(出价) | 4918705482605678398 |
邦格尔 | 4069896914521993236 |
Pangle(出价) | 3525379893916449117 |
PubMatic(出价) | 3841544486172445473 |
预订型广告系列 | 7068401028668408324 |
RhythmOne(出价) | 2831998725945605450 |
Rubicon(出价) | 3993193775968767067 |
SK 星球 | 734341340207269415 |
Sharethrough(出价) | 5247944089976324188 |
Smaato(出价) | 3362360112145450544 |
Equativ(出价)* * 在 2023 年 1 月 12 日之前,该广告联盟称为“Smart Adserver”。 | 5970199210771591442 |
Sonobi(出价) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy(出价) | 4692500501762622178 |
Tencent GDT | 7007906637038700218 |
TripleLift(出价) | 8332676245392738510 |
Unity 广告 | 4970775877303683148 |
Unity Ads(出价) | 7069338991535737586 |
Verizon Media | 7360851262951344112 |
Verve Group(出价) | 5013176581647059185 |
Vpon | 1940957084538325905 |
Liftoff Monetize* * 在 2023 年 1 月 30 日之前,该广告联盟称为“Vungle”。 | 1953547073528090325 |
Liftoff Monetize(出价)* * 在 2023 年 1 月 30 日之前,该广告联盟称为“Vungle(出价)”。 | 4692500501762622185 |
Yieldmo(出价) | 4193081836471107579 |
YieldOne(出价) | 3154533971590234104 |
Zucks | 5506531810221735863 |
獎勵使用者
決定時,應在使用者體驗和獎勵驗證之間取得平衡 何時應獎勵使用者伺服器端回呼可能會在下列時間之前發生延遲: 才能觸及外部系統因此,建議的最佳做法就是 用戶端回呼以立即獎勵使用者,同時執行 並在收到伺服器端回呼時驗證所有獎勵。這個 的做法能提供良好的使用者體驗,同時確保授予的 獎勵。
但對於獎勵有效性至關重要的應用程式 (例如 獎勵會影響應用程式的遊戲內經濟生態) 及提供獎勵的時間延遲 接受,等待已驗證的伺服器端回呼可能是最佳選擇 。
自訂資料
如果應用程式需要在伺服器端驗證回呼中使用額外資料,則應使用
獎勵廣告的自訂資料功能獎勵廣告中設定的任何字串值
系統會將物件傳遞至 SSV 回呼的 custom_data
查詢參數。如果答案為「否」
如果已設定自訂資料值,custom_data
查詢參數將不會
傳遞到 SSV 回呼中。
以下程式碼範例示範如何在 已載入獎勵廣告。
private void LoadRewardedAd(string adUnitId)
{
// Send the request to load the ad.
AdRequest adRequest = new AdRequest();
RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>
{
// If the operation failed with a reason.
if (error != null)
{
Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
return;
}
var options = new ServerSideVerificationOptions
.Builder()
.SetCustomData("SAMPLE_CUSTOM_DATA_STRING")
.Build()
rewardedAd.SetServerSideVerificationOptions(options);
});
}
如要設定自訂獎勵字串,請先完成相關設定再顯示 廣告。
手動驗證獎勵兩步驟驗證
RewardedAdsVerifier
類別為了驗證獎勵而執行的步驟
SSV 請見下方說明。雖然其中加入的程式碼片段位於 Java 和
請使用 Tink 第三方程式庫,您可以在
自選語言,使用任何支援
ECDSA:
擷取公開金鑰
如要驗證獎勵 SSV 回呼,您需要 AdMob 提供的公開金鑰。
如要驗證獎勵 SSV 回呼,可以列出一份公開金鑰清單, 從 AdMob 鍵中擷取 伺服器。公開金鑰清單 是以 JSON 表示法提供,其格式與下列內容類似:
{
"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=="
},
],
}
若要擷取公開金鑰,請連線至 AdMob 金鑰伺服器並下載
鍵。下列程式碼會完成這項工作並儲存 JSON
代表 data
變數的鍵。
String url = ...;
NetHttpTransport httpTransport = new NetHttpTransport.Builder().build();
HttpRequest httpRequest =
httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));
HttpResponse httpResponse = httpRequest.execute();
if (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {
throw new IOException("Unexpected status code = " + httpResponse.getStatusCode());
}
String data;
InputStream contentStream = httpResponse.getContent();
try {
InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);
data = readerToString(reader);
} finally {
contentStream.close();
}
請注意,系統會定期輪替公開金鑰。我們會透過電子郵件通知你 即將進行輪換快取公開金鑰時 主要金鑰
擷取公開金鑰之後,必須加以剖析。
下方的 parsePublicKeysJson
方法採用 JSON 字串,例如範例
並將這些值做為輸入內容,並將 key_id
值和公開金鑰的對應關係建立對應關係。
並在 Tink 程式庫中封裝為 ECPublicKey
物件。
private static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = new HashMap<>();
try {
JSONArray keys = new JSONObject(publicKeysJson).getJSONArray("keys");
for (int i = 0; i < keys.length(); i++) {
JSONObject key = keys.getJSONObject(i);
publicKeys.put(
key.getInt("keyId"),
EllipticCurves.getEcPublicKey(Base64.decode(key.getString("base64"))));
}
} catch (JSONException e) {
throw new GeneralSecurityException("failed to extract trusted signing public keys", e);
}
if (publicKeys.isEmpty()) {
throw new GeneralSecurityException("No trusted keys are available.");
}
return publicKeys;
}
取得內容以進行驗證
獎勵 SSV 回呼的最後兩個查詢參數一律為 signature
和 key_id,
。其餘查詢參數會指定
以進行驗證假設您已設定 AdMob 傳送獎勵回呼給
https://www.myserver.com/mypath
。以下程式碼片段是獎勵廣告範例
SSV 回呼,其中包含要驗證的內容。
https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins ×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887
下列程式碼示範如何剖析要從 做為 UTF-8 位元組陣列的回呼網址。
public static final String SIGNATURE_PARAM_NAME = "signature=";
...
URI uri;
try {
uri = new URI(rewardUrl);
} catch (URISyntaxException ex) {
throw new GeneralSecurityException(ex);
}
String queryString = uri.getQuery();
int i = queryString.indexOf(SIGNATURE_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a signature query parameter");
}
byte[] queryParamContentData =
queryString
.substring(0, i - 1)
// i - 1 instead of i because of & in the query string
.getBytes(Charset.forName("UTF-8"));
從回呼網址取得署名和 key_id
使用上一步驟的 queryString
值剖析 signature
並
回呼網址中的 key_id
查詢參數,如下所示:
public static final String KEY_ID_PARAM_NAME = "key_id=";
...
String sigAndKeyId = queryString.substring(i);
i = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a key_id query parameter");
}
String sig =
sigAndKeyId.substring(
SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);
int keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));
執行驗證
最後一步是使用
適用的公開金鑰取得從
parsePublicKeysJson
方法,並使用回呼中的 key_id
參數
從對應中取得公開金鑰的網址。然後使用
該公開金鑰以下步驟將如以下 verify
方法所示。
private void verify(final byte[] dataToVerify, int keyId, final byte[] signature)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();
if (publicKeys.containsKey(keyId)) {
foundKeyId = true;
ECPublicKey publicKey = publicKeys.get(keyId);
EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);
verifier.verify(signature, dataToVerify);
} else {
throw new GeneralSecurityException("cannot find verifying key with key ID: " + keyId);
}
}
如果方法在未擲回例外狀況的情況下執行,則回呼網址之前為 已成功驗證。
常見問題
- 我可以快取 AdMob 金鑰伺服器提供的公開金鑰嗎?
- 建議您快取 AdMob 金鑰提供的公開金鑰 ,減少驗證 SSV 所需的作業次數 回呼函式。不過請注意,公開金鑰會定期輪替,因此不應 超過 24 小時的快取時間。
- AdMob 金鑰伺服器提供的公開金鑰輪替頻率為何?
- AdMob 金鑰伺服器提供的公開金鑰會在變數上輪替 排程。為了確保 SSV 回呼的驗證持續運作, 公開金鑰的快取時間不應超過 24 小時。
- 如果無法連上我的伺服器,會發生什麼情況?
- Google 預期的
HTTP 200 OK
成功狀態回應代碼為 SSV 回呼函式。如果無法連線至伺服器,或是伺服器未提供 回應,Google 會在 間隔一秒。 - 如何驗證 SSV 回呼是否來自 Google?
- 使用反向 DNS 查詢,驗證 SSV 回呼是否來自 Google。