设备风险 SDK Android 接入

更新时间:
复制为 MD 格式

设备风险 SDK 用于采集 Android 设备指纹,识别设备风险。本文介绍从权限配置、依赖导入到初始化调用、Token 获取的完整接入流程,并提供代码示例和常见问题排查。

使用须知

重要

设备风险 SDK 需在 Android 4.0.3+(minSdkVersion 版本 15+)以上版本的系统运行。

Android SDK 使用限制如下:

  • 不支持模拟器模式调试。

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

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

关于 SDK 架构支持和体积说明,请参见常见问题

前提条件

  • 为落实集成第三方 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

否(推荐授予)

依赖配置

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

    说明
    • 单架构的 SO 文件在 2M 左右。

    • 设备风险识别 SDK 为了保证自身的抗逆向性以及数据在网络传输过程中的安全性,SDK 中有大量的插花、膨胀及加解密操作,所以 SDK 体积会相对较大。

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

    // 设备风险识别 SDK
    implementation files('libs/Android-AliyunDevice-版本号.aar')
    
    // 三方网络库依赖
    implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    implementation 'com.squareup.okio:okio:1.14.0'
    重要

    三方网络库不能省略,否则会造成设备风险识别 SDK 无法联网。

接口混淆配置

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

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

调用 SDK

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

  1. 初始化(initWithOptions)

  2. 获取客户端 Token(getDeviceToken)

  3. 携带 Token 请求服务端

1. 初始化(initWithOptions)

该函数用于完成信息采集。在进行风险识别时,需要在满足合规要求的情况下尽可能早地调用。

  • 函数原型

    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 域名。

    "1"

    CustomUrl

    设置数据上报服务器域名

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

    CustomHost

    设置数据上报服务器 host

    "cloudauth-device.aliyuncs.com"

    DataType

    设置不采集敏感数据的类型。

    默认为空:采集所有数据;

    具体可配置数据如下表。

    单选:

    NO_UNIQUE_DEVICE_DATA

    多选:

    NO_UNIQUE_DEVICE_DATA |

    NO_IDENTIFY_DEVICE_DATA

    敏感数据的类型

    说明

    设备信息字段详情

    NO_UNIQUE_DEVICE_DATA

    可变更唯一设备标识码

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

    NO_IDENTIFY_DEVICE_DATA

    不可变更唯一设备标识码

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

    NO_BASIC_DEVICE_DATA

    基础标识信息

    包括:设备名(Build.DEVICE)、Android 版本号(Build.VERSION#RELEASE)、屏幕分辨率。

    NO_EXTRA_DEVICE_DATA

    扩展敏感信息

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

    说明

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

    endpoints 默认服务地址:

    securityInitListener:初始化回调监听接口,可在回调中判断初始化是否成功,默认可以传入 null。其中,code 字段取值范围可参考状态返回值

    public interface SecurityInitListener {
        // code 表示接口调用状态码
        void onInitFinish(int code);
    }
  • 调用示例

    public class CustomApplication extends Application {
        private static String USER_PRODUCT_KEY = "123e4567e89b12d3a45642661417****";
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            Map<String, String> options = new HashMap<>();
            options.put("IPv6", "0"); // 设置为 IPv4
            //增加隐私数据采集开关,多选使用|进行或运算,再转成字符串。
            //options.put("DataType",  String.valueOf(NO_UNIQUE_DEVICE_DATA | NO_IDENTIFY_DEVICE_DATA));
            // 设置自定义的数据上报地域
            // options.put("CustomUrl", "xxx"); 设置上报站点 Url
            // options.put("CustomHost", "xxx"); 设置上报站点 Host
    
            // 标准调用(推荐)
            SecurityDevice.getInstance().initWithOptions(this, USER_PRODUCT_KEY,
                                                        options, null);
    
            // 回调调用
            SecurityDevice.getInstance().initWithOptions(this, USER_PRODUCT_KEY,
                                          options, new SecurityInitListener() {
                  @Override
                  public void onInitFinish(int code) {
                      if (SecurityCode.SC_SUCCESS != code) {
                          Log.d("AliyunDeviceRisk", "初始化失败");
                      } else {
                          Log.d("AliyunDeviceRisk", "初始化成功");
                      }
                  }
              });
        }
    }

2. 获取客户端 Token(getDeviceToken)

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

重要
  • 确保initWithOptions接口和getDeviceToken接口调用。

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

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

  • 函数原型

    public SecurityToken getDeviceToken();
    // 推荐传入 bizId
    public SecurityToken getDeviceToken(String bizId)
  • 参数

    bizId:客户的业务 ID,可用于关联业务 ID 和 token。默认情况可以不传。

  • 返回值

    SecurityToken 类型,定义如下:

    public class SecurityToken {
        // 接口调用状态码
        public int code;
    
        // 用于服务器端查询结果的 token 字符串。
        public String token;
    }
    • code:返回接口调用状态码,可用于判断接口调用是否成功。code 字段取值范围可参考状态返回值

    • token:返回 token 字符串信息,可用于业务后续查询阿里云设备风险识别接口。

      重要

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

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

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

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

      • 其次,确保 SDK 的initWithOptions接口和getDeviceToken接口调用。

  • 调用示例

    new Thread() {
        @Override
        public void run() {
            // 推荐传入 bizId,防止 deviceToken 被篡改。
            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();

3. 携带 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_EXTRA_DEVICE_DATA;

public class MainActivity extends AppCompatActivity {
  private static String USER_PRODUCT_KEY = "<请联系商务经理获取>";

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

      doStantard();
  }

    private void doStantard() {
        // 初始化 SDK
        // 在 App 生命周期中只需要调用 1 次
        // 建议在接入流程时机 1 调用 initWithOptions 接口
        doInit();

        // 此处等待 2s 仅保证数据上报完成
        // try {
        //   Thread.sleep(2000);
        // } catch (InterruptedException e) {
        //   e.printStackTrace();
        // }

        // 建议在接入流程时机 3 调用 getDeviceToken 接口
        new Thread() {
            @Override
            public void run() {
                // 获取 token
                doGetToken();
            }
        }.start();
    }

    private void doInit() {
        Map<String, String> options = new HashMap<>();
        options.put("IPv6", "0"); // 设置为 IPv4
        //增加隐私数据采集开关,多选使用|进行或运算,再转成字符串。
        //options.put("DataType",  String.valueOf(NO_UNIQUE_DEVICE_DATA | NO_IDENTIFY_DEVICE_DATA));
        SecurityDevice.getInstance().initWithOptions(this, USER_PRODUCT_KEY, options, null);
    }

    private void doGetToken() {
        // 推荐传入 bizId,防止 deviceToken 被篡改。
        String bizId = "1234567890abcdef1234567890ab****";
        SecurityToken deviceToken = SecurityDevice.getInstance().getDeviceToken(bizId);
        if(null == deviceToken || SecurityCode.SC_SUCCESS != deviceToken.code){
            Log.e("AliyunDevice", "获取 token 失败, code: " + deviceToken.code);
        } else {
            Log.d("AliyunDevice", "获取 token 成功, token: " + deviceToken.token);
        }
    }
}

调用风险识别 API 接口

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

常见问题

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