Para configurar regras baseadas em cenários anticrawler no gerenciamento de bots pelo console, integre o SDK de Proteção de Aplicativo do WAF. Este tópico explica como integrar o SDK a aplicativos Android.
Contexto
O SDK de Proteção de Aplicativo assina requisições originadas nos clientes do aplicativo. O servidor do Web Application Firewall (WAF) verifica essas assinaturas para identificar riscos, bloquear requisições maliciosas e proteger o aplicativo.
Limitações
No Android, o SDK suporta as arquiteturas arm64-v8a e armeabi-v7a.
O nível da API do Android deve ser 16 ou superior.
O método init pode demorar para concluir. Para garantir proteção completa, aguarde pelo menos 2 segundos entre a chamada do método init e do método vmpSign. Esse atraso é recomendado para otimizar a proteção, mas não é obrigatório. Ajuste-o conforme suas necessidades de negócio. Um intervalo menor pode impedir que os recursos de segurança funcionem com plena eficácia.
-
Ao usar o ProGuard para ofuscação de código, utilize a opção -keep para preservar os métodos do SDK. Exemplo:
-keep class com.aliyun.TigerTally.** {*;} -keep class com.aliyun.captcha.* {*;} -keepclassmembers,allowobfuscation class * { @com.alibaba.fastjson.annotation.JSONField <fields>; } -keep class com.alibaba.fastjson.** {*;}
Pré-requisitos
-
Obtenha o SDK para seu aplicativo Android.
Envie um ticket aos nossos especialistas técnicos de produto para obter o SDK.
NotaO SDK para Android inclui dois arquivos AAR: AliTigerTally_X.Y.Z.aar e AliCaptcha_X.Y.Z.aar, onde X.Y.Z representa o número da versão.
-
Obtenha a chave de autenticação do SDK (também conhecida como appkey).
Após ativar o Gerenciamento de Bots, acesse a página . Na lista de aplicativos, clique em Obtain and Copy AppKey para obter a chave de autenticação do SDK. Essa chave é necessária para a inicialização do SDK e deve constar no código de integração.
NotaCada conta Alibaba Cloud possui uma appkey exclusiva para todos os nomes de domínio protegidos pelo WAF. Use essa appkey na integração do SDK em aplicativos Android, iOS e HarmonyOS.
Exemplo de chave de autenticação:
****OpKLvM6zliu6KopyHIhmneb_****u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_****vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_****Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK****.
Etapa 1: Crie um projeto
No Android Studio, siga o assistente de configuração para criar um novo projeto Android. A figura abaixo mostra o diretório do projeto.

Etapa 2: Integrar o AAR
Extraia o arquivo do SDK tigertally-X.Y.Z-xxxxxx-android.tgz e copie todos os arquivos AAR da pasta extraída para o diretório libs do módulo principal (o caminho exato depende da configuração do projeto).

-
Abra o arquivo build.gradle do aplicativo e adicione dependências para AliTigerTally_X.Y.Z.aar e AliCaptcha_X.Y.Z.aar no diretório
libs.ImportanteSubstitua X.Y.Z nos nomes dos arquivos AliTigerTally_X.Y.Z.aar e AliCaptcha_X.Y.Z.aar pelo número de versão dos arquivos AAR.
A configuração segue este formato:
dependencies { // ... implementation files('libs/AliTigerTally_X.Y.Z.aar') implementation files('libs/AliCaptcha_X.Y.Z.aar') // third-party library dependencies implementation 'com.alibaba:fastjson:1.2.83_noneautotype' implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okio:okio:1.14.0' }
Etapa 3: Filtrar arquiteturas de CPU SO
Se o projeto ainda não utilizar arquivos SO, adicione a seguinte configuração ao arquivo build.gradle.
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a'
}
}
}
Etapa 4: Solicitar permissões
-
Permissão obrigatória
<uses-permission android:name="android.permission.INTERNET"/> -
Permissões opcionais
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
No Android 6.0 e versões posteriores, solicite dinamicamente as permissões android.permission.READ_EXTERNAL_STORAGE e android.permission.WRITE_EXTERNAL_STORAGE.
Etapa 5: Adicionar código de integração
1. Adicionar arquivos de cabeçalho
import com.alibaba.fastjson.*;
import com.aliyun.tigertally.*;
2. Configure a assinatura de dados
-
Defina um ID personalizado para cada usuário final. Isso permite configurar políticas de mitigação do WAF com maior flexibilidade.
/** * Sets the user account. * * @param account The user account. * @return An error code. */ public static int setAccount(String account)-
Parâmetros:
account: String. Identificador do usuário. Recomendamos o uso de um formato anonimizado.
Valor de retorno: int. Retorna 0 em caso de sucesso ou -1 em caso de falha.
-
Código de exemplo:
// For a guest, you can skip setAccount and directly initialize the SDK. After the user logs in, call setAccount and reinitialize. String account = "user001"; TigerTallyAPI.setAccount(account);
-
-
Inicialize o SDK e execute uma coleta inicial de dados.
A coleta inicial reúne informações do dispositivo uma única vez. Chame a função
initnovamente para realizar nova coleta conforme as necessidades do negócio. Existem três modos de coleta: completa, personalizada sem dados de privacidade e sem dados de privacidade. O modo sem dados de privacidade ignora campos relacionados à privacidade do usuário final, incluindo: imei, imsi, simSerial, wifiMac, wifiList, bluetoothMac e androidId.NotaSelecione um modo de coleta alinhado aos requisitos de conformidade e que garanta a integridade dos dados. Dados completos melhoram a identificação de riscos.
// Initialization callback public interface TTInitListener { // The code parameter indicates the status code of the interface call. void onInitFinish(int code); } /** * Initializes the SDK with a callback. * * @param context The application context. * @param appkey The SDK appkey. * @param collectType The data collection mode. * @param otherOptions Optional parameters. * @param listener The initialization callback. * @return An error code. */ public static int init(Context context, String appkey, int collectType, Map<String, String> otherOptions, TTInitListener listener);-
Parâmetros:
context: Context. Contexto da aplicação.
appkey: String. Appkey do SDK.
-
collectType: int. Modo de coleta. Valores válidos:
Parâmetro
Descrição
Exemplo
TT_DEFAULT
Coleta todos os dados.
TigerTallyAPI.TT_DEFAULT
TT_NO_BASIC_DATA
Não coleta dados básicos do dispositivo.
Inclui: nome do dispositivo (Build.DEVICE), versão do Android (Build.VERSION#RELEASE) e resolução de tela.
TigerTallyAPI.X | TigerTallyAPI.Y
(Indica que nem X nem Y são coletados. X e Y representam os nomes dos campos de itens específicos.)
TT_NO_IDENTIFY_DATA
Não coleta dados de identificação do dispositivo.
Inclui: IMEI, IMSI, SimSerial, BuildSerial (SN) e endereço MAC.
TT_NO_UNIQUE_DATA
Não coleta dados de identificadores exclusivos.
Inclui: OAID, Google Advertising ID e Android ID.
TT_NO_EXTRA_DATA
Não coleta dados estendidos do dispositivo.
Inclui: lista de aplicativos maliciosos/área cinzenta, IP LAN, IP DNS, informações de Wi-Fi conectado (SSID, BSSID), lista de Wi-Fi próximo, informações de localização e informações de sensor.
TT_NOT_GRANTED
Não coleta nenhum dos dados de privacidade mencionados anteriormente.
TigerTallyAPI.TT_NOT_GRANTED
-
otherOptions: Map<String, String>. Parâmetros opcionais de coleta de dados. Pode ser nulo. Parâmetros disponíveis:
Parâmetro
Descrição
Exemplo
IPv6
Define se deve usar um nome de domínio IPv6 para relatar informações do dispositivo.
-
0 (padrão): Usa um nome de domínio IPv4.
-
1: Usa um nome de domínio IPv6.
1
Intl
Define se as informações do dispositivo devem ser relatadas para uma região fora da China continental.
-
0 (padrão): Relata para a China continental.
-
1: Relata para uma região fora da China continental.
1
CustomUrl
Nome de domínio do servidor de relatório de dados.
https://cloudauth-device.us-west-1.aliyuncs.com
CustomHost
Host do servidor de relatório de dados.
cloudauth-device.us-west-1.aliyuncs.com
NotaPara a maioria dos sites internacionais, defina apenas o parâmetro Intl. Para relatar dados a um site específico, configure os parâmetros CustomUrl e CustomHost. Sites disponíveis:
Se
Intlfor0, os dados serão relatados ao site padrão na China (Xangai): https://cloudauth-device.cn-shanghai.aliyuncs.com-
Se
Intlfor1:Site padrão em Singapura: https://cloudauth-device.ap-southeast-1.aliyuncs.com
Indonésia (Jacarta): https://cloudauth-device.ap-southeast-5.aliyuncs.com
EUA (Vale do Silício): https://cloudauth-device.us-west-1.aliyuncs.com
Alemanha (Frankfurt): https://cloudauth-device.eu-central-1.aliyuncs.com
China (Hong Kong): https://cloudauth-device.cn-hongkong.aliyuncs.com
-
-
listener: TTInitListener. Callback de inicialização do SDK. Use o callback para verificar o status da inicialização. Pode ser nulo.
TTCode
Código
Descrição
TT_SUCCESS
0
SDK inicializado com sucesso.
TT_NOT_INIT
-1
SDK não inicializado.
TT_NOT_PERMISSION
-2
O SDK não recebeu as permissões necessárias do Android.
TT_UNKNOWN_ERROR
-3
Erro desconhecido do sistema.
TT_NETWORK_ERROR
-4
Erro de rede.
TT_NETWORK_ERROR_EMPTY
-5
Erro de rede: resposta vazia.
TT_NETWORK_ERROR_INVALID
-6
Formato de resposta de rede inválido.
TT_PARSE_SRV_CFG_ERROR
-7
Falha ao analisar a configuração do servidor.
TT_NETWORK_RET_CODE_ERROR
-8
O gateway retornou uma resposta de falha.
TT_APPKEY_EMPTY
-9
Appkey vazia.
TT_PARAMS_ERROR
-10
Parâmetro inválido.
TT_FGKEY_ERROR
-11
Erro de cálculo da chave.
TT_APPKEY_ERROR
-12
Incompatibilidade entre a versão do SDK e a appkey.
Valor de retorno: int. Retorna 0 em caso de sucesso ou -1 em caso de falha.
-
Código de exemplo:
// The appkey is the authentication key assigned on the Alibaba Cloud platform. final String appkey="******"; // Optional parameters to configure IPv6 and international reporting. Map<String, String> options = new HashMap<>(); options.put("IPv6", "0"); // Use IPv4. options.put("Intl", "0"); // Report to the Chinese mainland. //options.put("Intl", "1"); // Report to a region outside the Chinese mainland. // Report to US (Silicon Valley). //options.put("CustomUrl", "https://cloudauth-device.us-west-1.aliyuncs.com"); //options.put("CustomHost", "cloudauth-device.us-west-1.aliyuncs.com"); // An initial data collection gathers device information one time. You can call the init function again to perform a new collection based on your business requirements. // Full collection. int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, TigerTallyAPI.TT_DEFAULT, options, null); // Specify privacy data to collect. You can combine different flags by using the "|" operator. int privacyFlag = TigerTallyAPI.TT_NO_BASIC_DATA | TigerTallyAPI.TT_NO_UNIQUE_DATA; int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, privacyFlag, options, null); // Do not collect privacy fields. int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, TigerTallyAPI.TT_NOT_GRANTED, options, null); Log.d("AliSDK", "ret:" + ret);
-
-
Gere o hash dos dados.
Este método de assinatura personalizada calcula um hash dos dados de entrada e retorna a string
whashgerada como dados de assinatura personalizada. Para requisições POST, PUT e PATCH, passe o corpo da requisição. Para requisições GET e DELETE, passe a URL completa. Adicione a stringwhashao campo ali_sign_whash no cabeçalho da requisição HTTP.// Request type: public enum RequestType { GET, POST, PUT, PATCH, DELETE } /** * Hashes data for a custom signature. * * @param type The request type. * @param input The data to hash. * @return The whash string. */ public static String vmpHash(RequestType type, byte[] input);Parâmetros:
-
type: RequestType. Tipo de requisição. Valores válidos:
GET: requisição GET.
POST: requisição POST.
PUT: requisição PUT.
PATCH: requisição PATCH.
DELETE: requisição DELETE.
input: byte[]. Dados para gerar o hash.
Valor de retorno: String. Retorna a string
whash.-
Código de exemplo:
// GET request String url = "https://tigertally.aliyun.com/apptest"; String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.GET, url.getBytes()); Log.d("AliSDK", "whash:" + whash); // POST request String body = "hello world"; String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, body.getBytes()); Log.d("AliSDK", "whash:" + whash);NotaEste método não é necessário para a assinatura padrão. Para assinatura personalizada, chame este método para gerar o hash dos dados antes de assiná-los.
-
Assine os dados.
Este método utiliza a tecnologia VMP para assinar os dados de entrada e retorna uma string
wtokenpara autenticação da requisição./** * Signs the data. * * @param type The signature type. * @param input The data to sign. * @return The wtoken string. */ public static String vmpSign(int type, byte[] input);-
Parâmetros:
type: int. Tipo de assinatura de dados. O valor deve ser 1.
input: byte[]. Dados a serem assinados. Geralmente corresponde a todo o corpo da requisição ou à string whash da assinatura personalizada.
Valor de retorno: String. Retorna a string
wtoken.-
Código de exemplo:
// Use this code if default signing is configured in the console (custom signing is not selected). String body = "i am the request body, encrypted or not!"; String wtoken = TigerTallyAPI.vmpSign(1, body.getBytes("UTF-8")); Log.d("AliSDK", "wToken:" + wtoken); // Use this code if custom signing is configured in the console. // GET request String url = "https://tigertally.aliyun.com/apptest"; String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.GET, url.getBytes()); String wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes()); Log.d("AliSDK", "whash:" + whash + ", wtoken:" + wtoken); // POST request String body = "hello world"; String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, body.getBytes()); String wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes()); Log.d("AliSDK", "whash:" + whash + ", wtoken:" + wtoken);NotaAo chamar vmpHash para assinatura personalizada, o parâmetro input do método vmpSign corresponde à string
whashgerada. Ao configurar políticas antibot para aplicativos, defina o valor do Campo de Assinatura Personalizada comoali_sign_whash.Ao chamar vmpHash para gerar uma string
whashpara uma requisição GET, certifique-se de que a URL de entrada seja idêntica à URL final usada na requisição de rede. Preste atenção especial à codificação da URL, pois alguns frameworks codificam automaticamente caracteres chineses ou parâmetros.O parâmetro input do método vmpHash não aceita array de bytes vazio ou string vazia. Se a entrada for uma URL, ela deve conter um caminho ou parâmetros.
Ao chamar vmpSign, se o corpo da requisição estiver vazio (por exemplo, em uma requisição GET ou POST com corpo vazio), passe um objeto null ou o valor em bytes de uma string vazia, como "".getBytes("UTF-8").
-
Se o valor de
whashouwtokenfor uma das strings abaixo, o SDK retornou um erro:you must call init first: O SDK não foi inicializado. Chame
init()primeiro.you must input correct data: Dados de entrada inválidos.
you must input correct type: Tipo de entrada inválido.
-
3. Realizar verificação secundária
-
Avalie o resultado.
Verifique os campos cookie e body na resposta para determinar se a verificação secundária é necessária. Antes de chamar este método, mescle várias entradas Set-Cookie em uma única string de cookie.
/** * Determines whether to perform secondary verification. * * @param cookie The cookie string. * @param body The body string. * @return 0 for pass, or 1 for secondary verification required. */ public static int cptCheck(String cookie, String body)-
Parâmetros:
cookie: String. Todos os cookies da resposta HTTP.
body: String. Todo o corpo da resposta HTTP.
Valor de retorno: int. Retorna 0 se a requisição for aprovada ou 1 se a verificação secundária for necessária.
-
Código de exemplo:
String cookie = "key1=value1;kye2=value2;"; String body = "...."; int recheck = TigerTallyAPI.cptCheck(cookie, body); Log.d("AliSDK", "recheck:" + recheck);
-
-
Crie um slider.
Se cptCheck retornar 1, crie um objeto slider. O objeto TTCaptcha fornece os métodos
show()edismiss()para exibir e ocultar a janela do slider, respectivamente. TTOption encapsula os parâmetros configuráveis do slider, e TTListener fornece callbacks para estados de sucesso e falha. Para usar uma janela de slider personalizada, passe a URL da página personalizada. Arquivos HTML locais e páginas remotas são suportados./** * Creates a slider object. * * @param activity The activity where the slider is displayed. * @param option The slider parameters. * @param listener The callback for the slider. * @return The slider verification object. */ public static TTCaptcha cptCreate(Activity activity, TTOption option, TTListener listener); /** * The slider object. */ public class TTCaptcha { /** * Displays the slider. */ public void show(); /** * Hides the slider. */ public void dismiss(); /** * Gets the slider traceId for data statistics. */ public String getTraceId(); } /** * The slider parameters. */ public static class TTOption { // Specifies whether the slider can be dismissed by clicking a blank area. public boolean cancelable; // The custom page. Local HTML files and remote URLs are supported. public String customUri; // The language. public String language; } /** * The callback for slider events. */ public interface TTListener { /** * Called on successful verification. * * @param captcha The slider object. * @param data The token. Defaults to certifyId. */ void success(TTCaptcha captcha, String data); /** * Called on verification failure. * * @param captcha The slider object. * @param code The error code. */ void failed(TTCaptcha captcha, String code); }-
Parâmetros:
activity: Activity. Activity da página atual.
option: TTOption. Parâmetros de configuração do slider.
listener: TTListener. Callback de status do slider.
Valor de retorno: TTCaptcha. Objeto slider.
Código de exemplo:
TTCaptcha.TTOption option = new TTCaptcha.TTOption(); // option.customUri = "file:///android_asset/ali-tt-captcha-demo.html"; option.language = "cn"; option.cancelable = false; TTCaptcha captcha = TigerTallyAPI.cptCreate(this, option, new TTCaptcha.TTListener() { @Override public void success(TTCaptcha captcha, String data) { Log.d(TAG, "captcha check success:" + data); } @Override public void failed(TTCaptcha captcha, String code) { Log.d(TAG, "captcha check failed:" + code); } }); captcha.show();NotaUma falha na verificação indica que uma exceção foi detectada após o usuário concluir o desafio do slider.
Os códigos de erro são descritos abaixo:
1001: Falha na verificação.
1002: Exceção do sistema.
1003: Parâmetro inválido.
1005: Verificação cancelada.
8001: Falha ao exibir o slider.
8002: Dados anormais na verificação do slider.
8003: Exceção interna na verificação do slider.
8004: Erro de rede.
-
Exemplo de melhor prática
package com.aliyun.tigertally.apk;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.aliyun.TigerTally.TigerTallyAPI;
import com.aliyun.TigerTally.captcha.api.TTCaptcha;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class DemoActivity extends AppCompatActivity {
private final static String TAG = "TigerTally-Demo";
private final static String APP_HOST = "******";
private final static String APP_URL = "******";
private final static String APP_KEY = "******";
private final static OkHttpClient okHttpClient = new OkHttpClient();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
doTest();
}
private void doTest() {
Log.d(TAG, "captcha flow");
new Thread(() -> {
// Initialize the SDK.
Map<String, String> options = new HashMap<>();
//options.put("Intl", "1"); // To enable international reporting.
// Use full data collection mode.
int ret = TigerTallyAPI.init(this, APP_KEY, TigerTallyAPI.TT_DEFAULT, options, null);
// Alternatively, do not collect privacy fields.
// int ret = TigerTallyAPI.init(this, APP_KEY, TigerTallyAPI.TT_NOT_GRANTED, null, null);
Log.d(TAG, "tiger tally init: " + ret);
// Wait for initialization to complete.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Sign the data.
String data = "hello world";
String whash = null, wtoken = null;
// Use custom signing.
whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, data.getBytes());
wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
Log.d(TAG, "tiger tally vmp: " + whash + ", " + wtoken);
// Use standard signing.
// wtoken = TigerTallyAPI.vmpSign(1, data.getBytes());
// Log.d(TAG, "tiger tally vmp: " + wtoken);
// Send the request.
doPost(APP_URL, APP_HOST, whash, wtoken, data, (code, cookie, body) -> {
// Check if the slider is required.
int recheck = TigerTallyAPI.cptCheck(cookie, body);
Log.d(TAG, "captcha check result: " + recheck);
if (recheck == 0) return;
this.runOnUiThread(this::doShow);
});
}).start();
}
// Display the slider.
public void doShow() {
Log.d(TAG, "captcha show");
TTCaptcha.TTOption option = new TTCaptcha.TTOption();
// option.customUri = "file:///android_asset/ali-tt-captcha-demo.html";
option.language = "cn";
option.cancelable = false;
TTCaptcha captcha = TigerTallyAPI.cptCreate(this, option, new TTCaptcha.TTListener() {
@Override
public void success(TTCaptcha captcha, String data) {
Log.d(TAG, "captcha check success:" + data);
}
@Override
public void failed(TTCaptcha captcha, String code) {
Log.d(TAG, "captcha check failed:" + code);
}
});
captcha.show();
}
// Send the request.
public static void doPost(String url, String host, String whash, String wtoken, String body, Callback callback) {
Log.d(TAG, "start request post");
int responseCode = 0;
String responseBody = "";
StringBuilder responseCookie = new StringBuilder();
try {
Request.Builder builder = new Request.Builder()
.url(url)
.addHeader("wToken", wtoken)
.addHeader("Host", host)
.post(RequestBody.create(MediaType.parse("text/x-markdown"), body.getBytes()));
if (whash != null) {
builder.addHeader("ali_sign_whash", whash);
}
Response response = okHttpClient.newCall(builder.build()).execute();
responseCode = response.code();
responseBody = response.body() == null ? "" : response.body().string();
for (String item : response.headers("Set-Cookie")) {
responseCookie.append(item).append(";");
}
Log.d(TAG, "response code:" + responseCode);
Log.d(TAG, "response cookie:" + responseCookie);
Log.d(TAG, "response body:" + (responseBody.length() > 100 ? responseBody.substring(0, 100) : ""));
if (response.isSuccessful()) {
Log.d(TAG, "success: " + response.code() + ", " + response.message());
} else {
Log.e(TAG, "failed: " + response.code() + ", " + response.message());
}
response.close();
} catch (Exception e) {
e.printStackTrace();
responseCode = -1;
responseBody = e.toString();
} finally {
if (callback != null) {
callback.onResponse(responseCode, responseCookie.toString(), responseBody);
}
}
}
public interface Callback {
void onResponse(int code, String cookie, String body);
}
}