验证是否第一次登陆:
cookie
中的 deviceid
初始化,连接服务器后,发送:
{
"lwp": "/reg",
"headers": {
"cache-header": "token app-key did ua vhost wv",
"vhost": "WK",
"ua": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 OS(windows/6.1) Browser(chrome/63.0.3239.132) DingWeb/3.6.0 LANG/zh_CN",
"app-key": "85A09F60A599F5E1867EAB915A8BB07F",
"wv": "im:3,au:3,sy:4",
"did": "117851a2126f40aaa2f1a3e2df7c00dc",
"mid": "804b0002 0"
},
"body": null
}
- cache-header: 固定
- vhost:固定
- ua: 当前浏览器的
navigator.userAgent
- app-key:固定为
85A09F60A599F5E1867EAB915A8BB07F
- wv: 固定
- did:
cookie
没有 deviceid
时,由客户端生成,详见下面的 getDid(uuid.v4标准)
;否则不传该字段,但连接 WebSocket
的 HTTP
请求头应该有包含 Cookie:deviceid=7fc0930dba774fcfa7e1d7aac56fabcd; deviceid_exist=true;
- mid: genMid() + " 0"
var i = 0
, r = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
function genMid() {
var e = Math.floor(Math.random() * Math.pow(2, 16))
, t = i++
, n = "";
return n += r[e >> 12 & 15],
n += r[e >> 8 & 15],
n += r[e >> 4 & 15],
n += r[15 & e],
n += r[t >> 12 & 15],
n += r[t >> 8 & 15],
n += r[t >> 4 & 15],
n += r[15 & t]
};
function uuid_v4 () {
var o = [], i = [], l = {};
for (var e, t = 0; t < 16; t++)
0 === (3 & t) && (e = 4294967296 * Math.random()),
o[t] = e >>> ((3 & t) << 3) & 255;
for (var u = 0; u < 256; u++)
i[u] = (u + 256).toString(16).substr(1)
var n = 0;
return i[o[n++]] + i[o[n++]] + i[o[n++]] + i[o[n++]] +
"-" + i[o[n++]] + i[o[n++]] + "-" + i[o[n++]] + i[o[n++]] + "-" + i[o[n++]] + i[o[n++]] + "-" +
i[o[n++]] + i[o[n++]] + i[o[n++]] + i[o[n++]] + i[o[n++]] + i[o[n++]]
}
function getDid () {
return uuid_v4().replace(/\-/g, "");
}
对应PHP代码:
function getMid () {
static $i = 0;
$r = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
$e = rand(1, 65535);
$i++;
return $r[$e >> 12 & 15] .
$r[$e >> 8 & 15] .
$r[$e >> 4 & 15] .
$r[15 & $e] .
$r[$i >> 12 & 15] .
$r[$i >> 8 & 15] .
$r[$i >> 4 & 15] .
$r[15 & $i];
}
function uuid_v4() {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
function getDid () {
return str_replace('-', '', uuid_v4());
}
服务器响应注册消息:
{
"headers": {
"mid": "804b0002 0",
"sid": "ac1d503a5a55bf1454bb2724ef29f2f5882550192a20"
},
"code": 200
}
输入密码登陆:
{
"lwp": "/r/Adaptor/LoginI/login",
"headers": {
"mid": "2634000f 0"
},
"body": [
{
"title": "Windows 7 Web", // 最长 20 字节
"model": "Windows 7",
"token": "C1515568916640551275078381515568916640194"
},
"+86-13900000000", // 登陆手机号
"这是明文密码",
"85A09F60A599F5E1867EAB915A8BB07F", // app-key
null
]
}
服务器响应登陆成功的消息:
{
"headers": {
"dt": "j",
"mid": "2634000f 0",
"sid": "ac1d503a5a55bf1454bb2724ef29f2f5882550192a20"
},
"code": 400,
"body": {
"reason": "你在新的设备登录钉钉,为了保障你的账户安全,需要使用短信验证码确认",
"code": "14001"
}
}
客户端要求发送验证码:
{
"lwp": "/r/Adaptor/LoginI/sendSmsCode",
"headers": {
"mid": "691e0010 0"
},
"body": [
"+86-13900000000" // 登陆手机号
]
}
输入验证码:
{
"lwp": "/r/Adaptor/LoginI/tokenLogin",
"headers": {
"mid": "b75d0023 0"
},
"body": [
{
"title": "Windows 7 Web",
"model": "Windows 7",
"token": "C1515568916640551275055381515568916640194"
},
"+86-13900000000", // 登陆手机号
"1111", // 短信验证码
"85A09F60A599F5E1867EAB915A8BB07F", //app-key
"0",
null
]
}
用户登陆成功,服务器响应:
{
"headers": {
"dt": "j",
"mid": "b75d0023 0",
"sid": "ac1d503a5a55bf1454bb2724ef29f2f5882550192a20"
},
"code": 200,
"body": {
"nickPinyin": "qi",
"syncProtocol": false,
"tokenId": "B:45cde49ed06c55fc758fbe1b4a7e7777",
"openId": 33606666,
"secretToken": "8reAacn5g=",
"accessToken": "Gs/Go7jpo=",
"expiredTime": 1516865322664,
"nick": "昵称",
"domain": "dingding",
"tmpCode": "33405583_E755ADF8-E5A3-42DA-A60C-5555FCFE30B7",
"appKey": "85A09F60A599F5E1867EAB915A8BB07F",
"timestamp": "16124915505A584255406478",
"userProfileExtensionModel": {},
"refreshToken": "c2/kt52S8pvemA=="
}
}
使用上一步得到的 body.tmpCode
请求服务器设置 cookie
GET https://static.dingtalk.com/media/setCookie?code=33405583_E755ADF8-E5A3-42DA-A60C-5555FCFE30B7&callback=__jp0
订阅消息
{
"lwp": "/subscribe",
"headers": {
"token": "Gs/Go7jpo=", // 登陆成功后服务器推送消息中的 body.accessToken
"sync": "0,0;0;0;",
"set-ver": "0",
"mid": "ab030024 0"
}
}
服务端响应订阅:
{
"headers": {
"reg-sid": "ac1d503a5a55bf1454bb2724efdfdfdfdf5882550192a20",
"reg-uid": "33400003@dingding",
"mid": "ab030024 0",
"uuid": "ggHaACRjNjc2ZmQ3jU5LTQ0MTEtOGQwMS02YWRjYODMCsTMzNDA2NjgzQGRpbmdkaW5n",
"real-ip": "61.139.59.61",
"sid": "ac1d503a5a55bf1454bb2724ef29f2f5882550192a20"
},
"code": 200
}
获取状态
{
"lwp": "/r/Sync/getState",
"headers": {
"mid": "e4d10004 0"
},
"body": [
{
"pts": 0,
"highPts": 0,
"seq": 0,
"timestamp": 0,
"tooLong2Tag": ""
}
]
}
服务端响应获取的状态结果:
{
"headers": {
"dt": "j",
"mid": "e4d10004 0", // 和上面获取状态时发送的 mid 一样,表明是响应的数据
"sid": "0b8319955a57146440a3207de6bf969793baf23b6490"
},
"code": 200,
"body": {
"tooLong2Tag": "center,1",
"topic": "sync",
"highPts": 1515655826286000,
"pts": 1515623661092000,
"seq": 0,
"timestamp": 1515656293128
}
}
猜测是获取未读消息,经过这两步后,新消息才会主动通知给客户端
{
"lwp": "/r/Sync/getDiff",
"headers": {
"mid": "2d7b0013 0"
},
"body": [
//这里的数据就是上一步响应的结果
{
"tooLong2Tag": "center,1",
"topic": "sync",
"highPts": 1515655826286000,
"pts": 1515623661092000,
"seq": 0,
"timestamp": 1515656293128
}
]
}
确认收到未读消息
{
"lwp": "/r/Sync/ackDiff",
"headers": {
"mid": "022b0015 0"
},
"body": [
// 还是上面获取状态时的响应结果
{
"tooLong2Tag": "center,1",
"topic": "sync",
"highPts": 1515655826286000,
"pts": 1515623661092000,
"seq": 0,
"timestamp": 1515656293128
}
]
}
标记消息为已读
{
"lwp": "/r/IDLMessageStatus/updateToView",
"headers": {
"mid": "6e900034 0"
},
"body": [
"33666666:3833666665", // 上一步得到的 发送者uid:接收者uid
49827229684 // 上一步得到的消息ID
]
}