Web Application Firewall (WAF) コンソールで Harmony アプリのクローラー対策ルールを設定する前に、Anti-Bot SDK をアプリに統合する必要があります。このトピックでは、Anti-Bot SDK を Harmony アプリに統合する方法について説明します。このトピックでは、Anti-Bot SDK を SDK と呼びます。
背景情報
アプリ保護 SDK は、アプリから開始されたリクエストに署名します。その後、WAF サービスはこれらのリクエストの署名を検証して脅威を検出し、悪意のあるリクエストをブロックして、アプリサービスを保護します。
制限事項
SDK は HarmonyOS Next 4.1 以降で実行する必要があります。サポートされる最小 API バージョンは 12 です。
init 操作には時間のかかるアクションが含まれます。init 操作の直後に vmpSign 操作を呼び出すと、vmpSign 操作は失敗します。vmpSign 操作は、init 操作の少なくとも 2 分後に呼び出されるようにしてください。
cptCreate メソッドは UI 操作を伴うため、メインスレッドで呼び出す必要があります。
シミュレーターでのデバッグはサポートされていません。
バイトコードのパッケージングのみがサポートされています。
前提条件
Harmony 用の SDK を入手済みであること。
Harmony 用の SDK を入手するには、チケットを送信してください。
説明Harmony 用の SDK パッケージには、HarmonyOS-AliTigerTally-X.Y.Z-date.har という名前の HAR ファイルが含まれています。X.Y.Z はバージョン番号を、date はパッケージング日を指します。
SDK 認証キー (appkey) を取得済みであること。
BOT 管理を有効にした後、 ページに移動します。アプリリストで、AppKey の取得と複製 をクリックして SDK 認証キーを取得します。このキーは SDK の初期化リクエストに必要であり、統合コードに含める必要があります。
説明各 Alibaba Cloud アカウントには、WAF によって保護されているすべてのドメイン名に適用される一意の appkey があります。この appkey は、Android、iOS、および Harmony アプリケーションでの SDK 統合に使用されます。
認証キーの例: ****OpKLvM6zliu6KopyHIhmneb_****u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_****vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_****Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK****。
手順 1: プロジェクトの作成
このトピックでは、DevEco Studio を例として使用します。構成ウィザードを使用して Harmony プロジェクトを作成します。次の図は、プロジェクトディレクトリを示しています。
手順 2: HAR パッケージの統合
HarmonyOS-AliyunTigerTally-X.Y.Z-date.tgz SDK パッケージを解凍し、抽出した HAR ファイルをプロジェクトの必要なディレクトリにコピーします。
公式の HarmonyOS ドキュメントで説明されているように、ファイルを libs ディレクトリに配置することをお勧めします。

アプリの oh-package.json5 ファイルを開き、dependencies セクションに aliyuntigertally と captcha のコンパイル依存関係を追加します。例:
重要aliyuntigertally-X.Y.Z-date.har の X.Y.Z を、ご使用の HAR ファイルのバージョン番号に置き換えてください。
{ ... "dependencies": { "aliyuntigertally": "file:../libs/aliyuntigertally-X.Y.Z-date.har", "captcha": "file:../libs/aliyuncaptcha-X.Y.Z-date.har", ... } }
手順 3: アプリに必要な権限の取得
次の表に、SDK ベースの保護のパフォーマンスを向上させるために必要な権限を示します。
権限 | 必須 | 説明 |
ohos.permission.INTERNET | はい | SDK がインターネットに接続することを許可します。SDK は、期待どおりに実行するためにインターネットに接続する必要があります。 |
ohos.permission.GET_NETWORK_INFO | はい | SDK がネットワークステータス情報を取得することを許可します。SDK は、ネットワークステータスに基づいてより良いサービスを提供できます。 |
ohos.permission.STORE_PERSISTENT_DATA | いいえ、ただし推奨 | アプリが永続データを保存することを許可します。これにより、デバイスフィンガープリントの安定性が向上します。 |
ohos.permission.DISTRIBUTED_DATASYNC | いいえ、ただし推奨 | SDK が複数のデバイスのステータスを検出して、複数デバイスの連携を容易にすることを許可します。これにより、セキュリティが向上します。 |
ohos.permission.APP_TRACKING_CONSENT | いいえ、ただし推奨 | SDK が広告主識別子 (IDFA) に関する情報を取得することを許可します。これにより、デバイス ID の安定性が向上します。 |
手順 4: 統合コードの追加
1. ヘッダーファイルの追加
import { TigerTallyAPI, TTCode } from 'aliyuntigertally';
import { TTCaptcha, TTCaptchaOption, TTCaptchaListener} from 'aliyuntigertally';2. データ署名の設定
WAF での保護ルールの構成を容易にするために、ユーザー ID を指定します。
/** * ユーザーアカウントを設定します。 * * @param account アカウント * @return エラーコード */ public static setAccount(account: string): numberパラメーター:
account: ユーザーを識別する文字列。データ型: 文字列。非識別化されたフォーマットを使用することをお勧めします。
戻り値: 設定が成功したかどうかを示します。データ型: 数値。値 0 は成功を示し、値 -1 は失敗を示します。
サンプルコード:
// ゲストは setAccount を呼び出す必要はなく、直接 SDK を初期化できます。ユーザーがログオンした後、setAccount を呼び出して SDK を再初期化する必要があります。 let account: string = "user001"; TigerTallyAPI.setAccount(account);
SDK を初期化し、初期収集を実行します。
初期化収集とは、デバイス情報が一度収集されることを意味します。必要に応じて
init関数を再度呼び出して、別の初期化収集を実行できます。/** * 初期化コールバック。 */ export interface TTInitListener { /** * SDK 状態コードコールバック。 * @param code */ onInitFinish: (code: number) => void; } /** * コールバック付きの SDK 初期化。 * * @param ctx * @param userAppKey * @param options 様々なパラメーター。 * @param listener */ public static init(ctx: Context, userAppKey: string, options: Map<string, string> | null, listener: TTInitListener | null): numberパラメーター:
ctx: アプリに渡されるコンテキストを指定します。データ型: context。
userAppkey: SDK 認証キーを指定します。データ型: 文字列。
options: 情報収集のためのオプションパラメーター。データ型: Map<String,String>。デフォルト値は null です。次の表に、オプションパラメーターを示します。
パラメーター
説明
例
IPv6
IPv6 ドメイン名を使用してデバイス情報を報告するかどうかを指定します。有効な値:
0 (デフォルト): IPv4 ドメイン名が使用されます。
1: IPv6 ドメイン名が使用されます。
1
Intl
中国本土以外で登録されたドメイン名を使用してデバイス情報を報告するかどうかを指定します。有効な値:
0: 中国本土に報告します (デフォルト値)。
1. 中国本土以外からの報告。
1
listener: SDK 初期化コールバックインターフェイス。データ型: TTInitListener。コールバックで初期化結果の特定のステータスを確認できます。デフォルト値は null です。
TTCode
コード
説明
TT_SUCCESS
0
SDK は初期化されています。
TT_NOT_INIT
-1
SDK は初期化されていません。
TT_NOT_PERMISSION
-2
SDK に必要な基本的な Harmony 権限が完全に付与されていません。
TT_UNKNOWN_ERROR
-3
不明なシステムエラーが発生しました。
TT_NETWORK_ERROR
-4
ネットワークエラーが発生しました。
TT_NETWORK_ERROR_EMPTY
-5
ネットワークエラーが発生し、戻り値が空の文字列です。
TT_NETWORK_ERROR_INVALID
-6
応答のフォーマットが無効です。
TT_PARSE_SRV_CFG_ERROR
-7
システムがサーバー設定を解析できませんでした。
TT_NETWORK_RET_CODE_ERROR
-8
ゲートウェイが応答を返せませんでした。
TT_APPKEY_EMPTY
-9
appkey パラメーターが空です。
TT_PARAMS_ERROR
-10
他のパラメーターが無効です。
TT_FGKEY_ERROR
-11
システムがキーを計算できませんでした。
TT_APPKEY_ERROR
-12
SDK バージョンがアプリキーのバージョンと一致しません。
戻り値: 初期化結果。データ型: 数値。値 0 は成功を示し、値 -1 は失敗を示します。
サンプルコード:
// appkey パラメーターは Alibaba Cloud によって割り当てられた認証キーを指定します。 const appkey: string = "******"; // オプションパラメーター。IPv6 ドメイン名または中国本土以外で登録されたドメイン名を使用してデバイス情報を報告するかどうかを指定できます。 let options: Map<string, string> = new Map<string, string>(); options.set("IPv6", "0");// IPv6 ドメイン名を使用します。 options.set("Intl", "0");// 中国本土で登録されたドメイン名を使用します。 // 初期収集。後続のタスクでデバイス情報を収集したい場合は、init 関数を再度呼び出します。 let ret: number = TigerTallyAPI.init(getContext(this), appkey, options, null);
データに署名します。
入力データに署名し、リクエスト認証用の wToken 文字列を返します。
/** * データに署名します。 * * @param type データ型 * @param input 署名するデータ * @return wToken */public static vmpSign(type: number, input: string): stringパラメーター:
type: 署名するデータの型を指定します。データ型: 数値。値を 0 に設定します。
input: 署名するデータを指定します。データ型: 文字列。リクエストボディを指定することをお勧めします。
戻り値: wToken 文字列を示します。
サンプルコード:
let body: string = "私はリクエストボディです。暗号化されているか否かにかかわらず!"; let wtoken: string = TigerTallyAPI.vmpSign(0, body);説明次のいずれかの wToken 文字列が返された場合、初期化中に例外が発生しました:
you must call init first: init 関数が呼び出されていません。
you must input correct data: 入力データが無効です。
you must input correct type: 入力データの型が無効です。
inner error: 内部システムエラーが発生しました。
3. 二次検証
結果を確認します。
応答の cookie および body フィールドに基づいて、二要素認証を実行するかどうかを決定します。ヘッダーには複数の Set-Cookie フィールドが含まれる場合があります。
/** * 二要素認証が必要かどうかを確認します。 * * @param cookie cookie * @param body body * @return 0: 不要。1: 必要。 */ public static cptCheck(cookie: string, body: string): numberパラメーター:
cookie: 応答のすべての cookie を指定します。データ型: 文字列。
body: 応答の body を指定します。データ型: 文字列。
戻り値: 二要素認証が必要かどうかを示します。値 0 は二要素認証が不要であることを示します。値 1 は二要素認証が必要であることを示します。データ型: 数値。
サンプルコード:
let cookie: string = "key1=value1;kye2=value2;"; let body: string = "...."; let recheck: number = TigerTallyAPI.cptCheck(cookie, body);
スライダーを作成します。
cptCheck から返された結果に基づいてスライダーオブジェクトを作成するかどうかを決定します。TTCaptcha オブジェクトは show および dismiss メソッドを提供し、これらはスライダーウィンドウの表示と非表示に対応します。TTCaptchaOption はスライダーの設定可能なパラメーターをカプセル化し、TTCaptchaListener はスライダーの 2 つのコールバック状態を含みます。
/** * スライダーを作成します。 * * @param ctx * @param option パラメーター。 * @param listener コールバック関数。 * @return スライダー。 */ public static cptCreate(ctx: UIContext, option: TTCaptchaOption, listener: TTCaptchaListener): TTCaptcha /** * スライダー。 */ export class TTCaptcha { /** * スライダーを表示します。 */ public show(): void /** * スライダーを非表示にします。 */ public dismiss(): void } /** * スライダーのパラメーター。 */ export class TTCaptchaOption { // 空のエリアをクリックしてスライダーを非表示にできるかどうかを指定します。 cancelable: boolean; } /** * コールバック関数。 */ export interface TTCaptchaListener { /** * 検証成功。 * * @param captcha スライダー。 * @param code デフォルト値はトレース ID です。 */ success: (captcha: TTCaptcha, code: string) => void; /** * 検証失敗または例外。 * * @param captcha スライダー。 * @param code エラーコード。 */ fail: (captcha: TTCaptcha, code: string) => void; }パラメーター:
ctx: 現在のページの UI コンテキストを指定します。データ型: UIContext。
option: スライダーのパラメーターを指定します。データ型: TTCaptchaOption。
listener: スライダーステータスのコールバック関数を指定します。データ型: TTCaptchaListener。
戻り値: スライダーを示します。データ型: TTCaptcha。
サンプルコード:
let option: TTCaptchaOption = new TTCaptchaOption(); // option.cancelable = false; let captcha: TTCaptcha = TigerTallyAPI.cptCreate(this.getUIContext(), option, { success: (captcha: TTCaptcha, code: string) => { console.log("captcha success:", code); captcha.dismiss(); }, fail: (captcha: TTCaptcha, code: string) => { console.log("captcha failed:", code); captcha.dismiss(); } }); captcha.show();説明cptCreate メソッドは UI 操作を伴うため、メインスレッドで呼び出す必要があります。
検証例外は、スライダーの読み込み時に例外が検出されたことを示します。検証失敗は、検証完了後に例外が検出されたことを示します。エラーコードは次のとおりです:
1001: 検証に失敗しました。
1002: システムエラーが発生しました。
1003: パラメーターエラーが発生しました。
1005: 検証はキャンセルされました。
8001: スライダーの呼び出しエラーが発生しました。
8002: 検証例外が発生しました。
8004: ネットワークエラーが発生しました。
例
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
import { TigerTallyAPI, TTCode } from 'aliyuntigertally';
import { TTCaptcha, TTCaptchaOption, TTCaptchaListener} from 'aliyuntigertally';
@Entry
@Component
struct Index {
build() {
...
}
aboutToAppear() {
this.onTest();
}
async onTest() {
const APP_KEY: string = "xxxxxx";
const APP_URL: string = "xxxxxx";
const APP_HOST: string = "xxxxxx";
// SDK を初期化します。
let options: Map<string, string> = new Map<string, string>();
options.set("IPv6", "0");// IPv6 ドメイン名を使用します。
options.set("Intl", "0");// 中国本土で登録されたドメイン名を使用します。
let retCode: number = TigerTallyAPI.init(getContext(this), APP_KEY, options, null);
console.log("TigerTally init:", retCode);
// SDK の同期呼び出しを禁止します。
const sleep = (duration: number) => {
return new Promise<void>(resolve => setTimeout(resolve, duration));
};
await sleep(2000);
// データに署名します。
let data: string = "私はリクエストボディです。暗号化されているか否かにかかわらず!";
let wtoken: string = TigerTallyAPI.vmpSign(0, data);
console.log("TigerTally vmpSign:", wtoken);
// 必要な操作を呼び出します。
this.doPost(APP_URL, APP_HOST, wtoken, data, (code, cookie, body) => {
// スライダーを表示するかどうかを確認します。
let recheck: number = TigerTallyAPI.cptCheck(cookie, body);
console.log("TigerTally captcha check:", recheck);
if (recheck === 0) return;
this.doShow();
});
}
// スライダーを表示します。
async doShow() {
console.log("Slider display");
let option: TTCaptchaOption = new TTCaptchaOption();
// option.cancelable = false;
let captcha: TTCaptcha = TigerTallyAPI.cptCreate(this.getUIContext(), option, {
success: (captcha: TTCaptcha, code: string) => {
console.log("captcha success:", code);
captcha.dismiss();
},
fail: (captcha: TTCaptcha, code: string) => {
console.log("captcha failed:", code);
captcha.dismiss();
}
});
captcha.show();
}
// リクエストを送信します。
async doPost(url: string, host: string, wtoken: string, body: string,
callback: (code: number, cookie: string, body: string) => void): Promise<void> {
let response_code: number = 0;
let response_body: string = "";
let response_cookie: string = "";
try {
let httpRequest = http.createHttp();
let response: http.HttpResponse = await new Promise<http.HttpResponse>((resolve, reject) => {
httpRequest.request(
url,
{
method: http.RequestMethod.POST,
header: {
"Content-Type": "text/x-markdown",
"User-Agent": "",
"Host": host,
"wToken": wtoken,
},
extraData: body.length > 0 ? body : undefined,
connectTimeout: 12000,
},
(err: BusinessError, data: http.HttpResponse) => {
if (!err) {
resolve(data);
} else {
reject(err);
}
httpRequest.destroy();
}
);
});
if (response != null) {
response_code = response.responseCode;
let success: boolean = (response_code === 200);
if (success) {
response_body = response.result ? response.result.toString() : "";
response_cookie = response.header["set-cookie"] ? response.header["set-cookie"].join(";") : "";
} else {
response_body = response.result ? response.result.toString() : "";
}
} else {
response_code = -1;
}
console.log("response code:", response_code);
console.log("response body:", response_body);
console.log("response cookie:", response_cookie);
} catch (error) {
console.log("response error:", error.code, error.message);
response_code = -1;
response_body = error.message;
} finally {
if (callback != null) {
callback(response_code, response_cookie, response_body);
}
}
}
}