Xác thực các lệnh gọi lại xác thực phía máy chủ (SSV)

Lệnh gọi lại xác minh phía máy chủ là các yêu cầu URL, với tham số truy vấn do Google mở rộng, được Google gửi đến một hệ thống bên ngoài để thông báo rằng người dùng sẽ được thưởng vì đã tương tác với quảng cáo có tặng thưởng hoặc quảng cáo xen kẽ có tặng thưởng. Lệnh gọi lại của SSV quảng cáo có tặng thưởng (xác minh phía máy chủ) cung cấp thêm một lớp bảo vệ chống lại giả mạo lệnh gọi lại phía máy khách để tặng thưởng cho người dùng.

Hướng dẫn này cho bạn biết cách xác minh lệnh gọi lại của SSV dành cho quảng cáo có tặng thưởng bằng cách sử dụng thư viện mật mã bên thứ ba Ứng dụng Java Java để đảm bảo rằng các thông số truy vấn trong lệnh gọi lại là giá trị hợp lệ. Mặc dù Tink được sử dụng cho mục đích của hướng dẫn này, nhưng bạn có thể sử dụng bất kỳ thư viện bên thứ ba nào có hỗ trợ ECDSA. Bạn cũng có thể kiểm tra máy chủ của mình bằng công cụ kiểm tra trong giao diện người dùng AdMob.

Hãy xem ví dụ đầy đủ này đang hoạt động bằng cách sử dụng khởi động Java spring.

Điều kiện tiên quyết

Sử dụng RewardedAdsVerifier từ thư viện Ứng dụng Java của Tink

Kho lưu trữ GitHub Ứng dụng Java cho Java bao gồm một lớp trình trợ giúp RewardedAdsVerifier để giảm mã cần thiết nhằm xác minh lệnh gọi lại của SSV dành cho quảng cáo có tặng thưởng. Khi sử dụng lớp này, bạn có thể xác minh URL gọi lại bằng mã sau.

RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
    .fetchVerifyingPublicKeysWith(
        RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
    .build();
String rewardUrl = ...;
verifier.verify(rewardUrl);

Nếu phương thức verify() thực thi mà không phát sinh trường hợp ngoại lệ, thì URL gọi lại đã được xác minh thành công. Phần Tặng thưởng cho người dùng nêu chi tiết các phương pháp hay nhất về thời điểm người dùng sẽ được thưởng. Để biết thông tin chi tiết về các bước được lớp này thực hiện để xác minh lệnh gọi lại của SSV dành cho quảng cáo có tặng thưởng, bạn có thể tìm hiểu trong mục Xác minh thủ công SS quảng cáo có tặng thưởng.

Tham số gọi lại của SSV

Lệnh gọi lại xác minh phía máy chủ chứa các tham số truy vấn mô tả lượt tương tác với quảng cáo có tặng thưởng. Dưới đây là danh sách tên, thông số mô tả và giá trị mẫu. Thông số được gửi theo thứ tự bảng chữ cái.

Tên thông số Mô tả Giá trị mẫu
mạng_quảng_cáo Giá trị nhận dạng nguồn quảng cáo cho nguồn quảng cáo đã thực hiện quảng cáo này. Bạn có thể xem danh sách tên nguồn quảng cáo tương ứng với giá trị mã nhận dạng trong phần Giá trị nhận dạng nguồn quảng cáo. 1953547073528090325
đơn vị quảng cáo Mã đơn vị quảng cáo AdMob được dùng để yêu cầu quảng cáo có tặng thưởng. 2747237135
dữ liệu tuỳ chỉnh Chuỗi dữ liệu tuỳ chỉnh do customRewardString cung cấp.

Nếu ứng dụng không cung cấp chuỗi dữ liệu tuỳ chỉnh, thì giá trị tham số truy vấn này sẽ không hiển thị trong lệnh gọi lại của SSV.

SAMPLE_CUSTOM_DATA_STRING
mã_khóa Khoá dùng để xác minh lệnh gọi lại của SSV. Giá trị này ánh xạ tới khóa công khai do máy chủ khóa AdMob cung cấp. 1234567890
khoản_phần_trị Số tiền thưởng như được chỉ định trong phần cài đặt đơn vị quảng cáo. 5
item_item Vật phẩm thưởng như được chỉ định trong cài đặt đơn vị quảng cáo. xu
Chữ ký Chữ ký cho lệnh gọi lại của SSV do AdMob tạo ra. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMcergJHYYW9k2_icM9LFMY
timestamp Dấu thời gian về thời điểm người dùng được tặng thưởng dưới dạng Thời gian bắt đầu của hệ thống tính bằng mili giây. 1507770365237823
transaction_id Giá trị nhận dạng được mã hoá hex duy nhất cho từng sự kiện cấp phần thưởng do AdMob tạo ra. 18fa792de1bca816048293fc71035638
user_id Giá trị nhận dạng người dùng do userIdentifier.

Nếu ứng dụng không cung cấp giá trị nhận dạng người dùng, thì thông số truy vấn này sẽ không hiển thị trong lệnh gọi lại của SSV.

1234567

Giá trị nhận dạng nguồn quảng cáo

Tên và mã nguồn quảng cáo

Tên nguồn quảng cáo ID nguồn quảng cáo
Aarki (đặt giá thầu)5240798063227064260
Tạo quảng cáo (đặt giá thầu)1477265452970951479
AdColony15586990674969969776
AdColony (không phải SDK) (đặt giá thầu)4600416542059544716
AdColony (đặt giá thầu)6895345910719072481
Câu lạc bộ AdFalcon##Adfalcon##Cau lac bo AdFalcon3528208921554210682
Mạng AdMob5450213213286189855
Kết quả AD10593873382626181482
Quảng cáo AMoAd17253994435944008978
Applovin1063618907739174004
Applovin (đặt giá thầu)1328079684332308356
Tăng cường biểu đồ2873236629771172317
Chocolate Platform (đặt giá thầu)6432849193975106527
Kênh chéo (MdotM)9372067028804390441
Sự kiện tùy chỉnh18351550913290782395
DT Exchange*
* Trước ngày 21 tháng 9 năm 2022, mạng này được gọi là "Fyber Marketplace".
2179455223494392917
Fluct (đặt giá thầu)8419777862490735710
Flurry3376427960656545613
Fyber*
* Nguồn quảng cáo này dùng để báo cáo trong quá khứ.
4839637394546996422
i-mobile5208827440166355534
Cải thiện kỹ thuật số (đặt giá thầu)159382223051638006
Index Exchange (đặt giá thầu)4100650709078789802
InMobi7681903010231960328
InMobi (đặt giá thầu)6325663098072678541
ironSource6925240245545091930
Leadbolt2899150749497968595
LG U+AD18298738678491729107
maio7505118203095108657
maio (đặt giá thầu)1343336733822567166
Media.net (đặt giá thầu)2127936450554446159
Quảng cáo tự quảng bá đã dàn xếp6060308706800320801
Meta Audience Network*
* Trước ngày 6 tháng 6 năm 2022, mạng này được gọi là "Facebook Audience Network".
10568273599589928883
Meta Audience Network (đặt giá thầu)*
* Trước ngày 6 tháng 6 năm 2022, mạng này được gọi là "Facebook Audience Network (đặt giá thầu)".
11198165126854996598
MobFox8079529624516381459
MoPub (đã ngừng hoạt động)10872986198578383917
Mục tiêu của tôi8450873672465271579
Nend9383070032774777750
ONE by AOL (Millennial Media)6101072188699264581
ONE do AOL (Nexage) sản xuất3224789793037044399
OpenX (đặt giá thầu)4918705482605678398
Pangle (đặt giá thầu)3525379893916449117
PubMatic (đặt giá thầu)3841544486172445473
Chiến dịch đặt chỗ7068401028668408324
RhythmOne (đặt giá thầu)2831998725945605450
Rubicon (đặt giá thầu)3993193775968767067
Hành tinh SK734341340207269415
Chia sẻ (đặt giá thầu)5247944089976324188
Smaato (đặt giá thầu)3362360112145450544
Equativ (đặt giá thầu)*

* Trước ngày 12 tháng 1 năm 2023, mạng này được gọi là "Máy chủ quảng cáo thông minh".

5970199210771591442
Sonobi (đặt giá thầu)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (đặt giá thầu)4692500501762622178
GDT của Tencent7007906637038700218
TripleLift (đặt giá thầu)8332676245392738510
Quảng cáo Unity4970775877303683148
UnrulyX (đặt giá thầu)2831998725945605450
Phương tiện Verizon7360851262951344112
Vpon1940957084538325905
Kiếm tiền tăng lên*

* Trước ngày 30 tháng 1 năm 2023, mạng này được gọi là "Vungle".

1953547073528090325
Kiếm tiền gia tăng (đặt giá thầu)*

* Trước ngày 30 tháng 1 năm 2023, mạng này được gọi là "Vungle (đặt giá thầu)".

4692500501762622185
Yieldmo (đặt giá thầu)4193081836471107579
YieldOne (đặt giá thầu)3154533971590234104
Điên5506531810221735863

Tặng thưởng cho người dùng

Điều quan trọng là phải cân bằng trải nghiệm người dùng và xác thực phần thưởng khi quyết định thời điểm tặng thưởng cho người dùng. Các lệnh gọi lại phía máy chủ có thể bị chậm trễ trước khi tiếp cận các hệ thống bên ngoài. Do đó, phương pháp hay nhất được đề xuất là sử dụng lệnh gọi lại phía máy khách để tặng thưởng cho người dùng ngay lập tức, đồng thời thực hiện xác thực trên tất cả các phần thưởng khi nhận được lệnh gọi lại phía máy chủ. Phương pháp này mang lại trải nghiệm tốt cho người dùng trong khi vẫn đảm bảo tính hợp lệ của các phần thưởng đã cấp.

Tuy nhiên, đối với các ứng dụng mà tính hợp lệ của phần thưởng là rất quan trọng (ví dụ: phần thưởng ảnh hưởng đến hoạt động kinh tế trong trò chơi của ứng dụng) và sự chậm trễ trong việc cấp phần thưởng có thể chấp nhận được, thì lệnh gọi lại phía máy chủ đã được xác minh có thể là phương pháp tốt nhất.

Dữ liệu tùy chỉnh

Những ứng dụng cần có thêm dữ liệu trong lệnh gọi lại của tính năng xác minh phía máy chủ nên sử dụng tính năng dữ liệu tuỳ chỉnh của quảng cáo có tặng thưởng. Bất kỳ giá trị chuỗi nào được đặt cho đối tượng quảng cáo có tặng thưởng đều sẽ được truyền đến tham số truy vấn custom_data cho lệnh gọi lại của SSV. Nếu bạn không đặt giá trị dữ liệu tuỳ chỉnh, thì giá trị tham số truy vấn custom_data sẽ không hiển thị trong lệnh gọi lại của SSV.

Mã mẫu sau đây minh hoạ cách đặt các tuỳ chọn của SSV sau khi quảng cáo có tặng thưởng được tải.

Swift

GADRewardedAd.load(withAdUnitID:"ca-app-pub-3940256099942544/1712485313",
                       request: request,
                       completionHandler: { [self] ad, error in
      if let error != error {
      rewardedAd = ad
      let options = GADServerSideVerificationOptions()
      options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING"
      rewardedAd.serverSideVerificationOptions = options
    }

Objective-C

GADRequest *request = [GADRequest request];
[GADRewardedAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/1712485313"
                        request:request
              completionHandler:^(GADRewardedAd *ad, NSError *error) {
                if (error) {
                  // Handle Error
                  return;
                }
                self.rewardedAd = ad;
                GADServerSideVerificationOptions *options =
                    [[GADServerSideVerificationOptions alloc] init];
                options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING";
                ad.serverSideVerificationOptions = options;
              }];

Xác minh SSV dành cho quảng cáo có tặng thưởng theo cách thủ công

Các bước do lớp RewardedAdsVerifier thực hiện để xác minh SSV có tặng thưởng được nêu bên dưới. Mặc dù đoạn mã được sử dụng nằm trong Java và sử dụng thư viện Tink của bên thứ ba, nhưng bạn có thể triển khai các bước này theo ngôn ngữ mà mình muốn bằng cách sử dụng bất kỳ thư viện bên thứ ba nào có hỗ trợ ECDSA.

Tìm nạp khoá công khai

Để xác minh lệnh gọi lại của SSV dành cho quảng cáo có tặng thưởng, bạn cần có khoá công khai do AdMob cung cấp.

Bạn có thể tìm nạp danh sách các khoá công khai dùng để xác thực lệnh gọi lại của SSV dành cho quảng cáo có tặng thưởng từ máy chủ khoá AdMob. Danh sách khoá công khai được cung cấp dưới dạng biểu thức JSON có định dạng tương tự như sau:

{
 "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=="
    },
  ],
}

Để truy xuất khoá công khai, hãy kết nối với máy chủ khoá AdMob và tải các khoá xuống. Mã sau đây giúp hoàn thành công việc này và lưu phép biểu diễn JSON của các khóa đó vào biến 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();
}

Lưu ý các khóa công khai thường xuyên được xoay. Bạn sẽ nhận được email thông báo về việc xoay vòng sắp tới. Nếu đang lưu khoá công khai vào bộ nhớ đệm, bạn nên cập nhật khoá khi nhận được email này.

Sau khi tìm nạp, các khóa công khai phải được phân tích cú pháp. Phương thức parsePublicKeysJson dưới đây sẽ lấy chuỗi JSON (như ví dụ ở trên) làm dữ liệu nhập và tạo mối liên kết từ giá trị key_id đến khoá công khai. Các khoá này sẽ được đóng gói dưới dạng đối tượng ECPublicKey trong thư viện Tink.

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;
}

Yêu cầu nội dung cần xác minh

Hai tham số truy vấn cuối cùng của lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng luôn có thứ tự là signaturekey_id,. Các tham số truy vấn còn lại chỉ định nội dung cần được xác minh. Giả sử bạn đã định cấu hình AdMob để gửi lệnh gọi lại phần thưởng đến https://www.myserver.com/mypath. Đoạn mã dưới đây cho thấy một ví dụ về lệnh gọi lại SSV có tặng thưởng với nội dung cần xác minh.

https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins
&timestamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887

Đoạn mã bên dưới minh hoạ cách phân tích cú pháp nội dung cần được xác minh từ URL gọi lại dưới dạng mảng byte 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"));

Nhận chữ ký và key_id từ URL gọi lại

Sử dụng giá trị queryString từ bước trước, phân tích cú pháp các tham số truy vấn signaturekey_id từ URL gọi lại như sau:

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()));

Thực hiện xác minh

Bước cuối cùng là xác minh nội dung của URL gọi lại bằng khoá công khai phù hợp. Hãy lấy mối liên kết được trả về từ phương thức parsePublicKeysJson và sử dụng thông số key_id từ URL gọi lại để lấy khoá công khai từ mối liên kết đó. Sau đó, hãy xác minh chữ ký bằng khoá công khai đó. Các bước này được minh họa dưới đây trong phương thức 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);
  }
}

Nếu phương thức này thực thi mà không gửi ngoại lệ, thì URL gọi lại đã được xác minh thành công.

Câu hỏi thường gặp

Tôi có thể lưu khoá công khai do máy chủ khoá AdMob cung cấp vào bộ nhớ đệm không?
Bạn nên lưu khoá công khai do máy chủ khoá AdMob cung cấp vào bộ nhớ đệm để giảm số lượng thao tác mà bạn phải thực hiện để xác thực lệnh gọi lại của SSV. Tuy nhiên, xin lưu ý rằng hệ thống sẽ thường xuyên xoay vòng các khoá công khai, do đó, bạn chỉ nên lưu các khoá công khai vào bộ nhớ đệm trong thời gian quá 24 giờ.
Máy chủ khóa AdMob do xoay vòng của AdMob cung cấp bao lâu một khóa công khai?
Khoá công khai do máy chủ khoá AdMob cung cấp được xoay vòng theo lịch biểu thay đổi. Để đảm bảo rằng quy trình xác minh lệnh gọi lại của SSV tiếp tục hoạt động như dự kiến, bạn không nên lưu các khóa công khai vào bộ nhớ đệm quá 24 giờ.
Điều gì sẽ xảy ra nếu không thể kết nối với máy chủ của tôi?
Google dự kiến sử dụng mã phản hồi trạng thái thành công HTTP 200 OK cho lệnh gọi lại của SSV. Nếu không thể kết nối với máy chủ của bạn hoặc không cung cấp phản hồi dự kiến, Google sẽ thử gửi lại các lệnh gọi lại SSV tối đa 5 lần trong khoảng thời gian một giây.
Làm cách nào để xác minh rằng lệnh gọi lại của SSV là của Google?
Hãy dùng tính năng tra cứu DNS ngược để xác minh rằng các lệnh gọi lại của SSV là của Google.