基础篇请看:设备指纹系列--基础篇
我们接着前文继续写关于设备指纹前端接入方面的内容。话不多说,直接步入正题。
我们会在下文展示5种前端接入的方式,包括web接入、安卓接入、ios接入、微信小程序接入以及支付宝小程序接入。
在页面 HTML 中引入 const-id.js
,代码形如:
1 | <script src = "https://cdn.dingxiang-inc.com/ctu-group/constid-js/index.js" >< / script> |
因js文件会定期更新,为避免js失效影响您的使用,请不要将js下载到本地服务器上引入
页面加载后,初始化设备指纹,需要在 JavaScript 中调用 _dx.ConstID(options, callback)
方法获取设备指纹token,代码形如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var options = { appId: '【这里填写 AppID】' , / / 唯一标识,必填 server: 'https://constid.dingxiang-inc.com/udid/c1' , / / constId 服务接口,可选 userId: '【这里填写 userID】' / / 用户标识,可选 }; _dx.ConstID(options, function (err, token) { if (err) { / / console.log( 'error: ' + err); return ; } / / console.log( 'const-id token is ' + token); }); |
同时也支持Promise的用法
1 2 3 4 5 | _dx.ConstID(options).then(function(token) { console.log(token) }).catch(function(err) { console.log(err) }) |
字段 | 类型 | 是否必填 | 说明 |
| :------ | :------ | :--- | :----------------------------------- |
| appId | String | 是 | 当前应用的标识 |
| server | String | 否 | constId 服务接口,可选,如不填,则默认会用云服务接口 |
| scene | String | 否 | 场景标识,例如 login
、survey
等 |
| userId | String | 否 | 业务方的用户唯一标识,例如用户名、用户ID、手机号、Email等 |
| timeout | number | 否 | 超时失败时间,单位为毫秒 |
| cache | boolean | 否 | 默认为 true,表示会缓存采集结果;改为 false 则每次会重新采集
浏览器 | 最低版本 |
---|---|
IE | 8 |
Edge | 20 |
Chrome | 60 |
Safari | 11 |
Firefox | 60 |
360 | 10 |
Sougou | 8 |
4 |
浏览器 | 最低版本 |
---|---|
Chrome | 60 |
UC | 12 |
8 | |
Safari | 11 |
原生 | 安卓4.0及以上 |
条目 | 说明 |
---|---|
开发目标 | Android 4.0+ |
开发环境 | Android Studio 3.0.1 或者 Eclipse + ADT |
CPU架构 | ARM 或者 x86 |
SDK三方依赖 | 无 |
点击下载demo(仅做代码配置演示使用)
SDK包集成内容:
Demo工程结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | . ├── app │ ├── build.gradle │ ├── libs │ │ └── dx - risk - vx.x.xxr.xxxxxxxx.aar │ ├── proguard - rules.pro │ └── src │ └── main ├── build.gradle ├── gradle ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle |
1 2 3 4 5 6 7 8 9 10 | android { packagingOptions { doNotStrip "**/lib*Risk*.so" } } dependencies { implementation fileTree( dir : 'libs' , include: [ '*.aar' ]) } |
1 2 3 4 5 6 7 8 9 10 | <uses - permission android:name = "android.permission.INTERNET" / > <uses - permission android:name = "android.permission.ACCESS_NETWORK_STATE" / > <uses - permission android:name = "android.permission.ACCESS_WIFI_STATE" / > <uses - permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" / > <uses - permission android:name = "android.permission.READ_EXTERNAL_STORAGE" / > <uses - permission android:name = "android.permission.READ_PHONE_STATE" / > <uses - permission android:name = "android.permission.ACCESS_COARSE_LOCATION" / > <uses - permission android:name = "android.permission.ACCESS_FINE_LOCATION" / > <uses - permission android:name = "android.permission.ACCESS_BACKGROUND_LOCATION" / > <uses - permission android:name = "android.permission.BLUETOOTH" / > |
1 2 3 4 5 6 7 8 9 10 | - dontskipnonpubliclibraryclassmembers - keepattributes * Annotation * ,EnclosingMethod - dontwarn com.dx.mobile. * * - dontwarn * .com.dx.mobile. * * - dontwarn * .com.mobile.strenc. * * - keep class com.dx.mobile.risk. * * { * ;} - keep class com.security.inner. * * { * ;} - keep class * .com.dx.mobile. * * { * ;} - keep class * .com.mobile.strenc. * * { * ;} |
需要动态申请权限如下:
1 2 3 4 5 | android.permission.WRITE_EXTERNAL_STORAGE android.permission.READ_EXTERNAL_STORAGE android.permission.READ_PHONE_STATE android.permission.ACCESS_COARSE_LOCATION android.permission.ACCESS_FINE_LOCATION |
动态申请代码实例(Activity下):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_demo); / / API 23 或以上的动态申请权限 if (Build.VERSION.SDK_INT > = Build.VERSION_CODES.M) { String[] permissionArray = { "android.permission.ACCESS_COARSE_LOCATION" , "android.permission.ACCESS_FINE_LOCATION" , "android.permission.ACCESS_BACKGROUND_LOCATION" , "android.permission.WRITE_EXTERNAL_STORAGE" , "android.permission.READ_EXTERNAL_STORAGE" , "android.permission.READ_PHONE_STATE" , }; this.requestPermissions(permissionArray, 1 ); } } @Override public void onRequestPermissionsResult( int requestCode, String[] permissions, int [] grantResults) { super .onRequestPermissionsResult(requestCode, permissions, grantResults); / / start getToken new Thread(new Runnable() { @Override public void run() { HashMap<String, String> params = new HashMap<String, String>(); String token = DXRisk.getToken( "appid" , params); } }).start(); } |
采集端的设备指纹信息,上传至风控后台,再由风控后台返回token。 该API为耗时操作,因此必须在非主线程上调用,否则会抛异常。
DXRisk.java
该类是DxRisk SDK的风控组件接口,负责采集本地信息并返回用户前端token。
自v6.1.18r开始,SDK
setup前,可以调用setAllowPrivacyList
进行隐私采集白名单的设置,默认关闭所有涉及隐私规范红线的采集项
1 | public static void setAllowPrivacyList( long privacyFlag) |
使用示例
1 | setAllowPrivacyList(PrivacyFlag.ANDROID_ID); / / 只采集ANDROID_ID |
1 2 | / / 自定义列表模式 setAllowPrivacyList(PrivacyFlag.X | PrivacyFlag.Y | ...); / / 表示既采集X又采集Y,X、Y表示具体某项采集的FLAG值,如下表 |
以下为提供的白名单采集项
FLAG | 描述 |
---|---|
DEFAULT | 默认值,关闭所有隐私采集项 |
ALL | 打开所有隐私采集项 |
IMEI | 加入该FLAG则采集手机IMEI值,否则不采集 |
IMSI | 加入该FLAG则采集手机IMSI值,否则不采集 |
MEID | 加入该FLAG则采集手机MEID值,否则不采集 |
SERIAL_NUMBER | 加入该FLAG则采集手机SERIAL_NUMBER值,否则不采集 |
MAC_ADDRESS | 加入该FLAG则采集手机MAC_ADDRESS值,否则不采集 |
ICCID | 加入该FLAG则采集手机ICCID值,否则不采集 |
ANDROID_ID | 加入该FLAG则采集手机ANDROID_ID值,否则不采集 |
DEVICE_ID | 加入该FLAG则采集手机DEVICE_ID值,否则不采集 |
GET_INSTALLED_PACKAGES | 加入该FLAG则采集手机的应用安装列表,否则不采集 |
SDK
使用前必须调用先setup
,setup
主要用于数据/环境初始化,一般在Application
的onCreate
下调用:
1 2 3 4 5 6 | / * * * 初始化参数,环境 * @param context * @ return * / public static boolean setup(Context context) |
PS:下列两种方式获取token在网络通畅的情况下没有任何的不同。
1 2 3 4 5 | / * * * @ return token 通常返回长度为 40 的字符串。在网络卡顿或不通的情况下,返回 4 - 5k 的字符串。 * @throws DXRiskErrorException 如在主线程调用本API,或者appId为空等等,则会抛出该异常 * / public static String getToken(String appId, HashMap<String, String> paramsMap) throws DXRiskErrorException |
获取轻量级Token可获取的设备信息信息远少于getToken(),可能会造成在判断设备是否有风险时出现较大误差,请谨慎使用。
1 2 3 4 5 | / * * ` * @ return token 通常返回长度为 40 的字符串。在网络卡顿或不通的情况下,返回 1k 的字符串。 ` * @throws DXRiskErrorException 如在主线程调用本API,或者appId为空等等,则会抛出该异常 * / public static String getLightToken(String appId, HashMap<String, String> paramsMap) throws DXRiskErrorException |
建议Application.onCreate
下调用
1 2 3 4 5 6 | @Override public void onCreate() { super .onCreate(); / / 环境初始化 DXRisk.setup(this); } |
整个过程由于是耗时操作,必须要在非主线程上执行,否则会crash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | new Thread(){ @Override public void run() { / * 私有化配置 * / HashMap<String, String> params = new HashMap<String, String>(); / / 这里请填写对应的服务端url,注意SAAS和私有化的区别 params.put(DXRisk.KEY_URL, "https://constid.dingxiang-inc.com" ); / / 开启线上数据备份 / / params.put(DXRisk.KEY_BACKUP, DXRisk.VALUE_ENABLE_BACKUP); / / 设置请求token超时时长ms,不设置默认为 500ms params.put(DXRisk.KEY_DELAY_MS_TIME, "2000" ); / / 此配置关闭SDK缓存,表示每次调用接口都是强制采集信息 / / params.put( "PRIVATE_CLEAR_TOKEN" , "clear" ); / / 开通服务后可在应用管理菜单中获取;私有化版本请填写私有化appId String appId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ; / / 获取设备指纹token final String token = DXRisk.getToken(appId, params); / / TODO 把token通过Post请求,传到用户后端 } }.start(); |
在获取token过程中,如果因为网络超时或者加解密失败,该接口有可能会返回为null,同时会输出tag为DXRISK的错误信息,具体描述如下:
1 2 3 4 5 6 7 | DXRISK_REQUEST_NETWORK_ERR - 1001 / / 网络链接失败 DXRISK_REQUEST_DECRYPT_ERR - 1002 / / 数据解密错误 DXRISK_REQUEST_UNCOMPRESS_ERR - 1003 / / 解压错误 DXRISK_REQUEST_RESPONSE_EMPTY_ERR - 1004 / / 返回为空 DXRISK_REQUEST_DATA_PARSE_ERR - 1005 / / 数据解析失败 DXRISK_REQUEST_DIRTY_DATA_ERR - 1006 / / 脏数据 DXRISK_CONST_ID_EMPTY - 1007 / / constid为空 |
条目 | 说明 |
---|---|
兼容平台 | iOS 8.0+ |
开发环境 | XCode 4.0 + |
CPU架构 | armv7, arm64, i386, x86_64 |
SDK依赖 | libz, libresolv, libc++ , SystemConfiguration.framework , CoreLocation.framework , CoreTelephony.framework |
点击下载指纹SDK,SDK的目录结构如下:
dx-risk-iOS-x.x.x-xxxxxxx目录 DXRisk sdk
DXRisk.framework,
DXRiskWithIDFA.framework,
DXRiskStatic.framework,
DXRiskStaticWithIDFA.framework`其中之一直接拖入工程目录中,或者右击总文件夹添加文件。
DXRiskWithIDFA.framework
或者 DXRiskStaticWithIDFA.framework
,该版本可以提供更精准的tokenDXRisk.framework
或者 DXRiskStatic.framework
若在项目中添加DXRisk.framework
或者DXRiskWithIDFA.framework
其中之一,选择Target
-> General
,在Frameworks,Libraries,and Embedded Content
中,将DXRisk.framework
或者DXRiskWithIDFA.framework
对应的 Embed 切换到Embed & Sign。如下图:
若在项目中添加DXRiskStatic.framework
或者DXRiskStaticWithIDFA.framework
其中之一,需要在Build Settings -> Other Linker Flags 设置 -ObjC 如下图:
以下的操作仅限导入DXRisk.framework ,DXRiskWithIDFA.framework动态库
此步骤主要是解决上传Store架构不符合的问题,如项目中已配置过Carthage
或有其他相关的打包Framework调整脚本,可略过此步自行调整 选择Target
-> Build Phases
,点击+
按钮,添加如下脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | APP_PATH = "${TARGET_BUILD_DIR}/${WRAPPER_NAME}" # This script loops through the frameworks embedded in the application and # removes unused architectures. find "$APP_PATH" - name '*.framework' - type d | while read - r FRAMEWORK do FRAMEWORK_EXECUTABLE_NAME = $(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable) FRAMEWORK_EXECUTABLE_PATH = "$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME" echo "Executable is $FRAMEWORK_EXECUTABLE_PATH" EXTRACTED_ARCHS = () for ARCH in $ARCHS do echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME" lipo - extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" - o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH" EXTRACTED_ARCHS + = ( "$FRAMEWORK_EXECUTABLE_PATH-$ARCH" ) done echo "Merging extracted architectures: ${ARCHS}" lipo - o "$FRAMEWORK_EXECUTABLE_PATH-merged" - create "${EXTRACTED_ARCHS[@]}" rm "${EXTRACTED_ARCHS[@]}" echo "Replacing original executable with thinned version" rm "$FRAMEWORK_EXECUTABLE_PATH" mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH" done |
在iOS 12的环境最好在Capabilities->Access WiFi Information 进行开启操作,这样方便设备指纹数据采集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | / / 风控组件:DXRiskManager类 @interface DXRiskManager : NSObject / / 字符串常量 extern NSString * const DXRiskManagerKeyUserId; extern NSString * const DXRiskManagerKeyEmail; extern NSString * const DXRiskManagerKeyPhone; extern NSString * const DXRiskManagerKeyUserExtend1; extern NSString * const DXRiskManagerKeyUserExtend2; extern NSString * const DXRiskManagerKeyURL; / / 私有化服务器地址 extern NSString * const DXRiskManagerKeyBackup; / / 私有化下使用,将数据备份到顶象服务器(开启为DXRiskManagerKeyBackupEnable 值) extern NSString * const DXRiskManagerKeyBackupAppId; / / 私有化下使用,指定数据备份到顶象服务器的AppId extern NSString * const DXRiskManagerKeyDegradeNotify; / / 数据降级通知,若打开,服务端会有响应的降级统计 extern NSString * const DXRiskManagerKeyCountry; / / 国家地区设置,默认中国 extern NSString * const DXRiskManagerKeyDelayMsTime; / / 可填设置请求超时毫秒时间(默认值为 500 ,范围是:【 100 : 3000 】) / / NoticeDegrade参数 / * The NoticeDegrade Value. This value only be used pair with key:DXRiskManagerKeyDegradeNotify to notify token degrade. * / extern NSString * const DXRiskManagerKeyDegradeNotifyEnable; / / Backup参数 / * The Backup Value. This value only be used pair with key:DXRiskManagerKeyBackup to set data backup. * / extern NSString * const DXRiskManagerKeyBackupEnable; / / Country参数 / * The Country Value. This value only be used pair with key:DXRiskManagerKeyCountry to set country. * / extern NSString * const DXRiskManagerCountryChina; / * The Country Value. This value only be used pair with key:DXRiskManagerKeyCountry to set country. * / extern NSString * const DXRiskManagerCountryIndonesia; / * * 采集端的设备指纹信息,上传至风控后台,再由风控后台返回token。 该API为耗时操作,因此必须在非主线程上调用。 @param appId appId 开通服务后可在二级菜单“应用管理”中获取 @param extendsParams 配置项 @return token * / + (NSString * )getToken:(NSString * )appId extendParams:(NSDictionary * )extendsParams; / * * DXRiskManager - - 初始化方法 * / + ( BOOL )setup; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | / / 整个过程由于是耗时操作,必须要在非主线程上执行,否则会阻塞UI。如果本身已经在非UI线程上执行,则不需要另开线程 dispatch_queue_t dxrisk_queue = dispatch_queue_create( "com.dingxiang.dxrisk" , DISPATCH_QUEUE_CONCURRENT); dispatch_async(dxrisk_queue, ^{ NSMutableDictionary * dic = [NSMutableDictionary dictionary]; / / 根据业务逻辑,填充自定义字段 [dic setObject:@ "123456" forKey:DXRiskManagerKeyUserId]; / / 如需设置海外服务器, 可选值请参考头文件字段 [dic setObject:DXRiskManagerServiceAreaSoutheastAsia forKey:DXRiskManagerKeyServiceArea]; / / 如需自定义服务端URL,填充DXRiskManagerKeyURL字段(私有化部署的情况) / / 如有需要则添加DXRiskManagerKeyBackup参数,Value固定为DXRiskManagerKeyBackupEnable / / DXRiskManagerKeyBackupAppId参数可不填,参数为顶象备份数据库提供的备份Appid [dic setObject:@ "http://xxxxxxx" forKey:DXRiskManagerKeyURL]; / / [dic setObject:DXRiskManagerKeyBackupEnable forKey:DXRiskManagerKeyBackup]; [dic setObject:@ "xxxxxxxxxxxxxxxxxxxx" forKey:DXRiskManagerKeyBackupAppId]; / / 此配置关闭SDK缓存,表示每次调用接口都强制采集信息 / / [dic setObject:@ "clear" forKey:@ "PRIVATE_CLEAR_TOKEN" ]; / / 获取token / / 注意:token最好不要保存在某个局部变量或者字段,每次使用时,都通过API获取。 BOOL isSuccess = [DXRiskManager setup]; NSLog(@ "setup success: %@" , isSuccess ? @ "YES" :@ "NO" ); NSString * constID = [DXRiskManager getToken:@ "xxxxxxxxxxxxxxxxxxxx" extendsParams:dic]; NSLog(@ "constID: %@" , constID); / / TODO 把constid通过Post请求,传到业务后台。 / / 下面是模拟频繁调用的过程 while (TRUE) { NSLog(@ "constID: %@" , [DXRiskManager getToken:@ "xxxxxxxxxxxxxxxxxxxx" extendsParams:dic]); [NSThread sleepForTimeInterval:. 5 ]; } }); |
在获取token
过程中,如果因为网络超时或者加解密失败,该接口有可能会返回为null,同时会输出tag为DXRISK
的错误信息,具体描述如下:
1 2 3 4 5 6 7 | DXRISK_REQUEST_NETWORK_ERR - 1001 / / 网络链接失败 DXRISK_REQUEST_DECRYPT_ERR - 1002 / / 数据解密错误 DXRISK_REQUEST_UNCOMPRESS_ERR - 1003 / / 解压错误 DXRISK_REQUEST_RESPONSE_EMPTY_ERR - 1004 / / 返回为空 DXRISK_REQUEST_DATA_PARSE_ERR - 1005 / / 数据解析失败 DXRISK_REQUEST_DIRTY_DATA_ERR - 1006 / / 脏数据 DXRISK_CONST_ID_EMPTY - 1007 / / constid为空 |
请注意选择对应的版本,下载完成后放到本地项目中。
未注册用户可在顶象官网进行账号注册,创建应用获取应用密钥AppID和AppSecret。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | const ConstId = require( '本地设备指纹js存放路径' ) Page({ onLoad: function () { new ConstId({ appId: '【这里填写在顶象官网申请到的 AppID】' , / / 唯一标识,必填 server: 'https://host/udid/w1' / / 服务接口地址,请注意私有化客户填写自有域名 }, (e, id ) = > { if (e) { console.log(e) return } console.log( 'constId:' , id ) }) } }) |
https://constid.dingxiang-inc.com
,私有化用户配置本地部署域名,测试阶段可以开启微信开发者工具右上角详情->本地设置->“不校验域名”。接入方式和微信小程序类似,但是使用的SDK不一样。如有需要,请联系客服获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | const ConstId = require( '本地设备指纹js存放路径' ) Page({ onLoad: function () { new ConstId({ appId: '【这里填写在顶象官网申请到的 AppID】' , / / 唯一标识,必填 server: 'https://host/udid/w1' / / 服务接口地址,请注意私有化客户填写自有域名 }, (e, id ) = > { if (e) { console.log(e) return } console.log( 'constId:' , id ) }) } }) |
以上。
如果有设备指纹需求的话,可以前往顶象:免费试用