iOS DeviceCheck详解 / 2019-11-22

为保护用户的隐私,iOS系统的权限越来越收紧。而从开发者角度来说,能唯一标识一个设备对反作弊,风控的等都非常重要。从iOS 11开始,苹果提供了名为DeviceCheck的Framework来部分解决这类需求。

为保护用户隐私,iOS系统的权限越来越收紧(实际上Android系统也是如此,从Android 10开始将无法获取手机的imei信息,这会对以imei作为唯一设备标识的很多业务带来影响)。而从开发者角度来说,能唯一标识一个设备对反作弊,风控的等都非常重要。长久以来,开发者都在寻找各种关于设备的唯一标识,包括MAC、IDFV、IDFA、keychain字符串等,但他们都或多或少的有一些缺陷,比如删除APP、刷机等操作后将失效。

从iOS 11开始,苹果提供了名为DeviceCheck的Framework来部分解决这类需求,即使删除APP、刷机也不会使数据失效。DeviceCheck.framework接口非常简单,核心功能就是获取token。

import DeviceCheck

let device = DCDevice.current
if device.isSupported {
    device.generateToken { data, error in
        if let token = data?.base64EncodedString() {
            send token to your server
        }
    }
}

这个token每次获取都是不一样的。拿到token之后需要通过业务服务端和苹果服务端进行交互。交互流程参考文档:Accessing and Modifying Per-Device Data

通过苹果公开的接口,结合token,可以对设备的关联信息进行查询、更新、校验操作。API如下:

//查询
https://api.development.devicecheck.apple.com/v1/query_two_bits 
//更新
https://api.development.devicecheck.apple.com/v1/update_two_bits 
//校验
https://api.development.devicecheck.apple.com/v1/validate_device_token 

开发期间请求基地址使用https://api.development.devicecheck.apple.com,正式环境使用https://api.devicecheck.apple.com

各个接口的请求须使用JWT的格式(关于JWT的应用可以参考之前的文章:iOS Sign With Apple实践)。以查询接口为例,JSON web token的Payload中包含以下字段:

{
   "device_token" : "wlkCDA2Hy/CfrMqVAShs1BAR/0sAiuRIUm5jQg0a..."
   "transaction_id" : "5b737ca6-a4c7-488e-b928-8452960c4be9",
   "timestamp" : 1487716472000 
}

其中,device_token就是APP端device.generateToken生成的token;transaction_id是唯一字符串,可以随便定义,只要保证每一次请求都不同即可;timestamp是服务端当前的毫秒时间戳。请求成功时会返回如下结构:

{
   "bit0" : false,//第一位数据
   "bit1" : false,//第二位数据
   "last_update_time" : "2019-10-11"
}

也就是说实际上我们能操作的就是两个bit的数据,这就是为什么前面说是”部分解决这类需求“的原因。2bit数据,最多只能有4个状态,肯定不能作为设备唯一标识信息。但在一些场景下(比如标记是否享受过优惠 )还是很有用的。update_two_bits接口可以修改这两个bit的值。

其它文章

iOS 13 Scene Delegate and multiple windows
iOS crash log分析实践
Address Sanitizer的原理和使用
Thread Sanitizer的原理和使用
iOS DeviceCheck详解