前提:已获得 APNS 证书 ,已完成 MDM 配置描述文件的制作。请参考《 MDM 证书申请流程 》一文和《配置MDM Provisioning Profile》。
环境:OSX 10.9.2,JDK 1.6,Eclipse JavaEE Helois,Tomcat 7.0
一、前言
《THE IOS MDMPROTOCOL》(即Inside Apple MDM)一文中描述了一个简单 MDM Server Python 实现(server.py)。笔者也曾参照此文配置,但在安装M2Crypto 一步时遇到一个 cc 参数未定义错误,实在无法进行下去,因此不得不放弃。在参照《基于IOS上MDM技术相关资料整理及汇总》一文时,发现其使用了商业SSL证书(StartSSL),而笔者使用的自签名SSL证书,有些步骤不太一样 ,另外在一些关键点也需要读者自己摸索,因此有了本文的诞生。
二、准备
不管 APNS 还是 MDM,都需要服务器实现 https。假设我们使用 Eclipse 调试 Tomcat,则需要修改 Servers 项目下面的Tomcat 配置文件 server.xml。具体过程请参考《开启 Tomcat https 服务》。
三、 实现 checkin URL
MDM 需要实现完整 APNS 服务,对此我们采用的是第三方的 java apns 实现。主要是 notnoop 的 Java apns(不是google 的 JavaPNS),此外,还有 xmlwise ,用于解析苹果的 plist 文档。
java apns有两个包:apns-0.1.5.jar 和 apns-0.1.5-jar-with-dependencies.jar包。前者是 API,后者是依赖包。
xmlwise 就一个包:xmlwise-1_2.jar。
数据库采用 mysql,因此也需要 mysql-connector-java-5.1.2-bin.jar包。
此外java apns 使用了slf4j,即 slf4j-simple-1.7.7.jar。
将 MDM push 证书 mdm_push.p12 和 provisioning 配置描述文件 client.mobileconfig 放到WebContent 目录下。
用 MySQLWorkbench 连上 mysql 数据库,创建两张表,用于设备注册:
CREATE TABLE `Authenticate` (
`UDID` varchar(40) NOT NULL,
`Topic` varchar(200) DEFAULTNULL,
`timestamp` timestamp NULLDEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`UDID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `TokenUpdate` (
`UDID` varchar(40) NOT NULL,
`Topic` varchar(200) DEFAULTNULL,
`PushMagic` varchar(200)DEFAULT NULL,
`Token` varchar(200) DEFAULTNULL,
`UnlockToken` blob,
`timestamp` timestamp NULLDEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`UDID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
首先看注册服务。设备注册由Servlet checkin 实现。其 doPut 方法如下:
System.out.println("*********ReceivedMessage:***********\n"+plistStr);
try{
Map<String,Object>plist=Plist.fromXml(plistStr);
if(plist!=null){
StringMessageType=plist.get("MessageType").toString();
if(MessageType.equals("Authenticate")){
Authenticate au=newAuthenticate(plist.get("UDID").toString(),plist.get("Topic").toString());
au.save();
}elseif(MessageType.equals("TokenUpdate")){
Stringregex="Token</key>\\s*<data>\\s*([^\\s]*)\\s*</data>";
String tokenStr=Utils.regxCatch(plistStr,regex);
System.out.println("catches tokens:"+tokenStr);
TokenUpdate tu=new TokenUpdate(plist);
tu.Token=tokenStr;
tu.save();
}
response.getWriter().println(Utils.emptyPlist());
}
}catch(Exceptione){
e.printStackTrace();
}
checkin 主要处理与注册相关的两种消息:Authenticate 和 TokenUpdate。在设备注册中,服务器首先收到Authenticate 消息,checkin 将之计入 Authenticate 表,然后返回一个空的 plist 文件。设备随后会发来TokenUpdate 消息,checkin 也会将之存到 TokenUpdate 表并返回空 plist 文件。在 TokenUpdate 消息的处理中,checkin获取的是 UDID、device token、push magic和 unlock 等 MDM push中将要用到的重要字段。
其中,token(即 APNS 中的 device token)需要特别注意。因为在苹果的文档中说,这是一个32位长度的字符串。实际上我们都知道APNS 中,device token 是一个 byte 数组。在 TokenUpdate 消息中,iOS 将 device token 的 byte 数组进行了base64 编码,结果变成了一个 44 字节长度的 string。也就是说在 TokenUpdate 消息中,<token>字段的值类型应该是<string>,而不应该是消息中定义的<data>。一个典型的 TokenUpdate 消息,其 token 描述如下:
<key>Token</key>
<data> [ 32 byte string, base64 encoded,redacted ]</data>
显然,这里的<data>必须换成<string>,xmlwise 包中的 Plist 类才能正确解析。
因为在 Plist 类中,对于 plist 文件中的数据类型<data>会被解析为 byte 数组而不是字符串。
因此 checkin 在处理 TokenUpdate 消息时,采取了额外的手段来获取 token 字段,即正则捕获。
运行 Tomcat 服务器,将 iPad 接入服务器统一 wifi 网络,然后在浏览器中访问描述文件地址:
https://192.168.2.1:8443/mdmtest/client.mobileconfig
此时 safari 将调用设置程序,在客户端安装 mdm 配置描述文件。当你点击“安装”按钮,iPad 会请求 checkin URL地址,并发送Authenticate 消息和 TokenUpdate 消息。你可以在数据库中查看到这两条消息。