设备风险 SDK Android 接入

更新时间:
复制 MD 格式

本文档介绍了设备风险SDK (Android)的接入流程。

使用须知

设备风险 SDK 需在 Android 4.4 及以上(minSdkVersion 19+)系统上运行。

Android SDK 使用限制如下:

  • 仅支持 Android 4.4 及以上系统版本的移动智能设备(手机或 Pad)接入。

  • 目前支持 arm、armv7 和 arm64 三个架构。

前提条件

  • 为落实集成第三方 SDK 的隐私合规义务,降低隐私违规风险,请使用阿里云文档中心官网发布的最新版本产品。在使用设备风险识别前,请了解个人信息处理规定及《风险识别 SDK 隐私权政策》,并按照SDK 合规使用说明进行接入。

权限说明

SDK 需要以下权限以增强风险识别效果:

权限内容

是否必选

备注

android.permission.INTERNET

是

保证 SDK 可以联网。

android.permission.ACCESS_NETWORK_STATE

是

用于获取设备网络状态信息。

android.permission.READ_PHONE_STATE

否(推荐授予)

该部分权限在 Android 6.0 以上系统中需要动态获取。启用相关权限后,在接入 SDK 并调用initWithOptions初始化接口之前,确保 App 已被授予相关权限。

android.permission.WRITE_EXTERNAL_STORAGE

否(推荐授予)

android.permission.READ_EXTERNAL_STORAGE

否(推荐授予)

依赖配置

  • 下载Android SDK,并完成解压。SDK 为 Android 标准的.aar 包。

  • 单架构的 SO 文件在 2.5M 左右。

  • 设备风险识别 SDK 内置了较强的代码保护和数据加密机制,因此体积会相对变大。

  • 拷贝解压后的 aar 文件到工程的 libs 目录下,并在 App 的 build.gradle 中添加以下依赖关系:

// 设备风险识别 SDK
implementation files('libs/Android-AliyunDevice-版本号.aar')

接口混淆配置(重要)

如果工程使用了代码混淆,请在 App 工程的 proguard-rules.pro 文件中添加如下配置,防止接口被混淆后功能异常。

-keep class net.security.device.api.** {*;}
-dontwarn net.security.device.api.**

调用 SDK

完成上述配置后,按以下四个步骤完成客户端接入:

  • AndroidManifest.xml 配置(可选)

  • 初始化(initWithOptions)

  • 获取客户端 Token(getDeviceToken)

  • 携带 Token 请求服务端

1. AndroidManifest.xml 配置(可选)

<!--
    可选增强项:添加AppZygote进程与service服务可以提升风控检测能力。
    1、在已有的 <application> 标签上增加 android:zygotePreloadName 属性
    2、在 <application> 标签内增加一个 <service> 声明
-->
<application android:zygotePreloadName="net.security.device.api.compat.CompatPreloader"
  <service
    android:name="net.security.device.api.compat.CompatBridgeService"
    android:exported="false"
    android:isolatedProcess="true"
    android:useAppZygote="true" />
</application>

2. 初始化(initWithOptions)

该函数用于完成 SDK 初始化和信息采集。在进行风险识别时,需要在满足合规要求的情况下尽可能早地调用。该函数在一次 APP 启动后只需要调用一次。

  • 函数原型

public interface SecurityInitListener {
  // code 表示接口调用状态码
  void onInitFinish(int code);
}
public void initWithOptions(Context ctx, String appKey,
                            Map<String, String> options,
                            SecurityInitListener securityInitListener);
  • 参数说明

    • ctx:当前 Application Context,或 Activity Context。

    • appKey:用于标识用户身份,可在阿里云控制台的设备 App 管理申请获取。

    • options:信息采集可选项,默认为 null。可选参数如下。

      字段名

      说明

      示例

      IPv6

      是否使用 IPv6 域名上报设备信息。0(默认):使用 IPv4 域名。1:使用 IPv6 域名。

      "0"

      CustomUrl

      设置数据上报服务器域名。指定站点上报时使用,默认不需要设置。

      "https://cloudauth-device.aliyuncs.com"

      CustomHost

      设置数据上报服务器 host。需与 CustomUrl 配对使用,默认和 CustomUrl 一起不需要设置。

      "cloudauth-device.aliyuncs.com"

      DataType

      设置不采集设备数据的类型。默认为空(推荐),采集所有数据,具体可配置数据如下表。

      单选:NO_UNIQUE_DEVICE_DATA多选:NO_UNIQUE_DEVICE_DATA|NO_IDENTIFY_DEVICE_DATA

      DataType参数说明

      采集数据的类型

      说明

      设备信息字段详情

      NO_UNIQUE_DEVICE_DATA

      可变更唯一设备标识码

      包括:OAID、Google 广告 ID、Android ID。

      NO_IDENTIFY_DEVICE_DATA

      不可变更唯一设备标识码

      包括:IMEI、IMSI、SimSerial、BuildSerial(SN)、MAC 地址。

      NO_BASIC_DEVICE_DATA

      基础设备信息

      包括:系统版本、设备机型、屏幕分辨率。

      NO_EXTRA_DEVICE_DATA

      设备扩展信息

      包括:黑灰产 App 列表、局域网 IP、DNS IP、连接的 WIFI 信息(SSID、BSSID)、附近 WIFI 列表、定位(检测虚拟定位)。

      CustomUrl和CustomHost参数说明

      指定站点上报,需要设置 CustomUrl 和 CustomHost 为指定地域,默认情况不需要设置。

      地域

      地址

      国内(默认)

      CustomUrl:https://cloudauth-device.aliyuncs.com
      CustomHost:cloudauth-device.aliyuncs.com

  • 调用示例

public class CustomApplication extends Application {
  // 在阿里云-风险识别控制台-开发者中心-设备App管理中创建
  private static String appKey = "<请在控制台创建应用后获取>";

  @Override
  public void onCreate() {
    super.onCreate();

    Map<String, String> options = new HashMap<>();
    // 隐私采集开关:默认无需配置;如需端上范围关闭,多选用 | 或运算后转字符串
    // 如果只需关闭某个采集字段,而不是通过DataType参数范围关闭,也可以联系我们通过服务端下发关闭。
    // options.put("DataType",  String.valueOf(NO_UNIQUE_DEVICE_DATA | NO_IDENTIFY_DEVICE_DATA));
    // 设置自定义的数据上报地域(默认不需要设置,仅指定站点时使用)
    // options.put("CustomUrl", "https://cloudauth-device.aliyuncs.com");
    // options.put("CustomHost", "cloudauth-device.aliyuncs.com");

    // 方式一:标准调用(推荐,不关心初始化结果时使用)
    SecurityDevice.getInstance().initWithOptions(this, appKey, options, null);

    // 方式二:回调调用(需要监听初始化结果时使用)
    SecurityDevice.getInstance().initWithOptions(this, appKey, options, new SecurityInitListener() {
      @Override
      public void onInitFinish(int code) {
        if (SecurityCode.SC_SUCCESS != code) {
          Log.d("AliyunDeviceRisk", "初始化失败!Code=" + code);
        } else {
          Log.d("AliyunDeviceRisk", "初始化成功");
        }
      }
    });
  }
}

3. 获取客户端 Token(getDeviceToken)

获取客户端 token 并上报到业务服务器,后续通过服务器端服务端API接口接入获取设备风险信息。

重要
  • 确保initWithOptions接口和getDeviceToken接口调用时间间隔 2 秒以上。

  • 调用getDeviceToken时建议传入 bizId,可以将本次 token 和业务唯一 ID 绑定,然后在服务端查询结果时将 ID 一起传入,并确保客户端传入 bizId 和服务端传入 ID 一致,可校验 Token 被替换的风险。

  • 建议在 App非主线程上调用 getDeviceToken 接口,以避免接口调用耗时可能导致的崩溃。

  • 函数原型

public SecurityToken getDeviceToken();
// 推荐传入,可用于关联业务ID和deviceToken
public SecurityToken getDeviceToken(String bizId)

  public class SecurityToken {
  // 接口调用状态码
  public int code;

  // 用于服务器端查询结果的 token 字符串。
  public String token;
}
重要

token 字符串在网络环境良好的场景下,长度为 600 字节左右;在网络环境较差的场景下,可能返回弱网降级数据,长度在 2.5K 左右,并且带有特殊标识:

  • 国内:有网"Tkxxxx"、弱网"UFxxxx";

如果业务上出现了大量的长 token:

  • 首先,请确保客户端的网络是畅通的;

  • 其次,确保 SDK 的initWithOptions接口和getDeviceToken接口调用时间间隔 2 秒以上。

  • 调用示例

// 建议在非主线程调用,避免阻塞 UI 导致 ANR
new Thread() {
  @Override
  public void run() {
    // 推荐传入 bizId,将 token 与业务 ID 绑定。
    // 服务端查询时传入相同 bizId,可校验 token 是否被篡改、替换。
    String bizId = "1234567890abcdef1234567890ab";
    SecurityToken deviceToken = SecurityDevice.getInstance().getDeviceToken(bizId);
    if(null != deviceToken){
      if(SecurityCode.SC_SUCCESS == deviceToken.code){
        Log.d("AliyunDevice", "token: " + deviceToken.token);
      } else {
        Log.e("AliyunDevice", "getDeviceToken error, code: " + deviceToken.code);
      }
    } else {
      Log.e("AliyunDevice", "getDeviceToken is null.");
    }
  }
}.start();

4. 携带 Token 请求服务端

成功获取 deviceToken 后,将 deviceToken 作为参数传至业务服务端。由服务端调用阿里云设备风险识别 API 接口,传入 deviceToken 查询并校验设备风险信息。

状态返回值

SecurityCode

Code

备注

SC_SUCCESS

10000

SDK 初始化成功。

SC_NOT_INIT

10001

SDK 未初始化。

SC_NOT_PERMISSION

10002

SDK 需要的 Android 基础权限未完全授权。

SC_UNKNOWN_ERROR

10003

系统未知错误。

SC_NETWORK_ERROR

10004

网络错误。

SC_NETWORK_ERROR_EMPTY

10005

网络错误,返回内容为空串。

SC_NETWORK_ERROR_INVALID

10006

网络返回的格式非法。

SC_PARSE_SRV_CFG_ERROR

10007

服务端配置解析失败。

SC_NETWORK_RET_CODE_ERROR

10008

网关返回失败。

SC_APPKEY_EMPTY

10009

AppKey 为空。

SC_PARAMS_ERROR

10010

其他参数错误。

SC_FGKEY_ERROR

10011

密钥计算错误。

SC_APPKEY_ERROR

10012

SDK 版本和 AppKey 版本不匹配。

完整代码示例

import net.security.device.api.SecurityDevice;
import net.security.device.api.SecurityInitListener;
import net.security.device.api.SecurityToken;
import net.security.device.api.SecurityCode;
import static net.security.device.api.SecurityDevice.NO_UNIQUE_DEVICE_DATA;
import static net.security.device.api.SecurityDevice.NO_IDENTIFY_DEVICE_DATA;

public class MainActivity extends AppCompatActivity {
  // 在阿里云-风险识别控制台-开发者中心-设备App管理中创建
  private static final String APP_KEY = "<请在控制台创建应用后获取>";

  // initWithOptions 与 getDeviceToken 的最小调用间隔:国内站 2 秒,国际站 3 秒
  private static final long INIT_TO_TOKEN_INTERVAL_MS = 2000L;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 二选一:doInitWithSleep() 或 doInitWithCallback()
    doInitWithSleep();
    // doInitWithCallback();
  }

  // ==================== 方式一:保障时间差 ====================
  // 简单直接,不关心初始化结果时使用。
  private void doInitWithSleep() {
    doInit(null);

    new Thread(() -> {
      try {
        // 这里模拟等待一定时间,保障初始化完成,如果初始化没有完成就获取deviceToken会返回降级的deviceToken
        Thread.sleep(INIT_TO_TOKEN_INTERVAL_MS);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      // 获取deviceToken
      doGetToken();
    }).start();
  }

  // ==================== 方式二:初始化回调 ====================
  // 需要监听初始化结果时使用。回调 onInitFinish 在第一阶段初始化后触发,
  private void doInitWithCallback() {
    doInit(code -> {
      if (SecurityCode.SC_SUCCESS != code) {
        Log.e("AliyunDevice", "初始化失败, code: " + code);
        return;
      }
      // 初始化完成后,可以立刻调用获取 deviceToken
      doGetToken();
    });
  }

  /** 初始化 SDK(异步,App 生命周期内只调用 1 次,建议放在 Application.onCreate()) */
  private void doInit(SecurityInitListener listener) {
    Map<String, String> options = new HashMap<>();
    // 隐私采集开关:默认无需配置;如需端上范围关闭,多选用 | 或运算后转字符串
    // 如果只需关闭某个采集字段,而不是通过DataType参数范围关闭,也可以联系我们通过服务端下发关闭。
    // options.put("DataType",  String.valueOf(NO_UNIQUE_DEVICE_DATA | NO_IDENTIFY_DEVICE_DATA));
    SecurityDevice.getInstance().initWithOptions(this, APP_KEY, options, listener);
  }

  /** 获取 DeviceToken,建议在非主线程调用 */
  private void doGetToken() {
    // bizId 将 token 与业务 ID 绑定,服务端查询时传入相同 bizId 可校验防篡改
    // 生产环境请用真实业务流水号,不要写死
    String bizId = "1234567890abcdef1234567890ab";
    SecurityToken deviceToken = SecurityDevice.getInstance().getDeviceToken(bizId);
    if (null == deviceToken) {
      Log.e("AliyunDevice", "deviceToken is null");
    } else if (SecurityCode.SC_SUCCESS != deviceToken.code) {
      Log.e("AliyunDevice", "获取 token 失败, code: " + deviceToken.code);
    } else {
      Log.d("AliyunDevice", "获取 token 成功, token: " + deviceToken.token);
      // 将 deviceToken.token 传至业务服务端,由服务端调用风险识别 API
        }
    }
}

调用风险识别 API 接口

将 deviceToken 与其他参数,参考服务端API接口接入,请求风险识别 API 接口进行识别。

常见问题

关于设备风控 SDK 的常见问题,请参见SDK常见问题。