这几天在给单位做一个钉钉的微应用,其中涉及到一些钉钉的身份真证的东西。
网上找了一大圈没什么结果,官方文档也不怎么好理解。现在功能基本上做出来了就简单在这里梳理下 。
钉钉的身份获取是通过引入JS文件,经过授权后调用客户端容器里面的值进行身份的验证。这个和我之前测试云之家的接口是一样的。
这些官方文档看着都很头疼,至少当时金蝶那边还能够联系技术员。做钉钉的时候就只能死磕文档了。
钉钉的微应用引入JS分成两个部分 一块是手机端的 一块是桌面端的 ,两部分JS是不一样的。至于能不能放在一起我不是很清楚也没有尝试过。
手机端引入后会获得一个dd的全局变量
桌面端引入后货获得一个DingTalkPC的全局变量
变量下面调用的方法名是一样的,问了他们交流群里面的技术人员他们说只是有一些场景和交互上面的区别。由于没有做桌面端的东西所以没有什么直接的体会。
下面进入关注的重点
钉钉的用户身份是通过 "https://oapi.dingtalk.com/user/getuserinfo?access_token=access_token=token&code=code 这个接口获取的
其中accesstocken 是通过 https://oapi.dingtalk.com/gettoken?corpid=CorpId&corpsecret=CorpSecret 接口使微应用里面的CorpId和CorpSecret 获取,有效期是两个小时。
重点就是code这一块, 官方文档在这一部分写得相当的分散 绕了我好久。
之前提到的引入的全局变量后 调用之前需要进行dd.config()验证和授权功能
授权的功能只有在dd.ready()方法里面进行调用。而且我发现好像钉钉引入的JS文件屏蔽掉了一些JQuery的方法,比如$("#id").click() 这些是用不了的。事件只能够写function 重新进行一次dd.config()的授权。
dd.config({
appId: _config.appId,
corpId: _config.corpId,
timeStamp: _config.timeStamp,
nonceStr: _config.nonce,
signature: _config.signature,
jsApiList: ['runtime.info',
'biz.contact.choose',
'device.notification.confirm',
'device.notification.alert',
'device.notification.prompt',
'biz.ding.post',
'runtime.permission.requestAuthCode',
'device.geolocation.get',
'biz.ding.post',
'biz.contact.complexChoose',
'biz.telephone.call']
});
上面的代码就是dd.config()
其中appID是就是微应用设置里面的那个agentID
corpid是企业的ID和获取access_tocken的那个ID一样,
timeStamp当前时间的参数,nonceStr 随机字符串也很好理解。
最头痛的signature 是根据前面几项过去签名的算法,这个 我本来想去看看官方文档是怎么写的可惜我找了近10分钟没找到被官方藏在哪里了。。
好吧 我贴上我的代码来描述。
private void GetConfig()
{
appId = Config.EAgentID;
corpId = Config.ECorpId;
string corpSecret = Config.ECorpSecret;
nonceStr = Helper.randNonce();
timestamp = Helper.timeStamp();
string url = Request.Url.ToString();
//这里重新实现
accessToken = EnterpriseBusiness.GetToken(corpId, corpSecret).access_token;
jsApiTicket = EnterpriseBusiness.GetTickets(accessToken);
str = "jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}";
str = string.Format(str,jsApiTicket, nonceStr, timestamp, url);
signature = FormsAuthentication.HashPasswordForStoringInConfigFile(str, "SHA1").ToLower();
}
嗯 就是上面这段 ,先要通过access_token获取到jsapi_ticket,这个ticket只能使用一次 ,每次要重新获取。
接口在这里 https://oapi.dingtalk.com/get_jsapi_ticket?access_token=access_token
反正我看git上的文档和阿里的文档上面描述的参数不一样,但是似乎都是可以获取的git上面的老文档要多一个固定的常量参数type但是没什么用
url似乎不能带端口,我在测试的时候开了一个9999端口做测试。虽然映射到外网是可以直接访问地址是可以的,但是获取到的url参数依然是带了端口号的导致验证不通过。不知道是我哪里的理解有偏差,耽误了我好多时间 。改成80端口映射出去就ok了。
最后汇总字符串 文档上描述是根据参数的ascll码顺序排列abcdefg..自己数一遍就搞定。
通过验证之后就能够在dd. dd.runtime.permission.requestAuthCode() 中获取到code了。
拿到了code 带上之前的access_token就可以拿到用户信息了,
只是用户信息的userid是一个隐藏的id在钉钉的管理平台看不到,只有再通过https://oapi.dingtalk.com/user/get?access_token=access_token&userid=userid 获取用户信息的JSON。
下面是一部分JS
<script type="text/javascript">
var code = "";
var _userid="";
var _username = "";
var _dept = "";
var _isSign = "";
var _total = "";
var _returnValue = "";
var _returnNon = "";
var _config = {
appId: '<%=appId%>',
corpId: '<%=corpId%>',
timeStamp: '<%=timestamp%>',
nonce: '<%=nonceStr%>',
signature: '<%=signature%>',
str: '<%=str%>',
accessToken: '<%=accessToken%>'
};
dd.config({
appId: _config.appId,
corpId: _config.corpId,
timeStamp: _config.timeStamp,
nonceStr: _config.nonce,
signature: _config.signature,
jsApiList: ['runtime.info',
'biz.contact.choose',
'device.notification.confirm',
'device.notification.alert',
'device.notification.prompt',
'biz.ding.post',
'runtime.permission.requestAuthCode',
'device.geolocation.get',
'biz.ding.post',
'biz.contact.complexChoose',
'biz.telephone.call']
});
dd.ready(function () {
dd.runtime.permission.requestAuthCode({
corpId: _config.corpId,
onSuccess: function (result) {
/*{
code: 'hYLK98jkf0m' //string authCode
}*/
code = result.code;
//获取userid
$.ajax({
url: "../getusers.aspx?code=" + code + "&token=" + _config.accessToken,
method: "GET",
success: (function (data) {
var user = JSON.parse(data);
_userid = user.userid;
//$("#info").text(_userid);
//获取userinfo
$.ajax({
url: "../getuserinfo.aspx?id=" + _userid + "&token=" + _config.accessToken,
method: "GET",
success: (function (data) {
var userinfo = JSON.parse(data);
_username = userinfo.name;
//获取今日是否签到和已签到次数
$.ajax({
url: "../getSignTimes.aspx?userid=" + _userid,
method: "GET",
success: (function (data) {
var sign = JSON.parse(data);
//$("#test").text(data);
_isSign = sign.isSign;
_total = sign.totalTimes;
//界面初始
$("#name").text(_username);
if (_isSign =="0") {
//$("#today").text(_isSign + ";" + _total);
}
else {
$("#today").text("今日已签到,请明天再来");
$("#sub").hide();
}
$("#total").text("总共签到" + _total + "天");
//$("#today").text(_isSign + ";" + _total);
}),
error: (function (err) { })
});
}),
error: (function (err) {
$("#name").text(JSON.stringify(err));
})
});
}),
error: (function (err) {
$("#info").text(JSON.stringify(err));
})
});
},
onFail: function (err) { }
})
});
dd.error(function (error) {
dd.device.notification.alert({
message: "error",
title: "提示", //可传空
buttonName: "收到",
onSuccess: function () {
$("#str").text(_config.str);
$("#text").text(JSON.stringify(error));
},
onFail: function (err) { }
});
});
</script>
钉钉的方法里面都可以在onSuccess里面回调 和在onFail中捕获异常
有兴趣去啃官方文档的也可以去多看看 地址放在链接里面了。