您的位置:首页 > 其它

钉钉开发系列(八)二维码扫描登录的实现

2016-07-22 17:15 453 查看
钉钉PC版在登录的时候,需要使用手机版的来扫描,扫描之后会弹出一个确定的页面,确定之后PC端就会登录进去。基于此,我们也想实现相似的功能,为此我们需要先探讨其中的原理。

查看钉钉PC版的二维码会发现是一个含有一个KEY的URL,钉钉扫描后会直接进入该URL的页面,在页面中确定之后,会向钉钉服务器发起一个请求,应该是写入了一串值,同时钉钉PC版检测到该值,匹配之后就登录成功了。下面是一个流程简图。



由此,我们只需要实现同样的流程则可达到二维码登录的目的。

1.建立服务器

我们可以在数据库中建一个qrlogin的表来保存key,比如我们建这样的一个表



其中qrl_guid就是登录的key,登录后的标识flag就是qrl_dingDingUserId(钉钉用户的ID)。

2.生成二维码

二维码的生成可以使用相关的库,比如.net的可以用thoughtworks.qrcode.dll,生成的字符串是htttp://www.xxx.com/qrlogin.html?key=生成的GUID,代码如下

string enCodeString = "http://www.xxx.com/qrlogin.html?key=" + key;
QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
Bitmap qrBmp = qrCodeEncoder.Encode(enCodeString, Encoding.UTF8);
其中key需要先将其插入到数据库表TQRLogin中。

3.qrlogin.html页面

在使用钉钉扫一扫进入到qrlogin.html页面后,我们不只需要得到url中的key,还需要得到该用户的信息,为此需要jsapi的票据,然后配置要调用的jsapi,比如dd.runtime.permission.requestAuthCode,再通过该函数得到用户的信息,示例代码如下

var g={};
g.configJsSdk=function (jsApiList, fnReady, fnError) {
/// <summary>配置微信的JSSDK</summary>
/// <param name="jsApiList" type="function">要请求的api列表,以['biz.chat.chooseConversationByCorpId','biz.chat.toConversation']的形式</param>
/// <param name="fnReady" type="function">dd.ready调用的函数</param>
/// <param name="fnError" type="function">dd.error调用的函数</param>
var url = window.location.href;
$.getJSON("/Api/DDAuth/GetSignPackage", "url=" + window.location.href, function (response, textStatus, jqXHR) {
try {
if (response.Status != global.StatusCode.OK) {
alert('GetSignPackage error.' + response.Message);
return;
}
var signPackage = response.Data;
global.corpId = signPackage.corpId;
dd.config(
{
agentId: signPackage.agentId,
corpId: signPackage.corpId,
timeStamp: signPackage.timeStamp,
nonceStr: signPackage.nonceStr,
signature: signPackage.signature,
jsApiList: jsApiList
});
dd.ready(function () {
if (fnReady != null) {
fnReady();
}
});

dd.error(function (err) {
if (err == null) {
alert('dd error: ' + JSON.stringify(err));
}
else {
fnError();
}
});
}
catch (e) {
alert("getJSON error:" + JSON.stringify(e))
}
});
}

g.configJsSdk(
['dd.runtime.permission.requestAuthCode'],
function () {
dd.runtime.permission.requestAuthCode({
corpId: g.corpId,
onSuccess: function (result) {
result = eval(result);
$.getJSON("/Api/DDUser/GetUserWithCode", "code=" + result.code, function (response, textStatus, jqXHR) {
try {

if (response.Status != g.StatusCode.OK) {
alert("GetUserWithCode:" + response.Message);
return;
}
var userInfo = {
userid: response.Data.userid,
name: response.Data.name
};
localStorage.userInfo = JSON.stringify(userInfo);
} catch (e) {
alert(JSON.stringify(e));
}
});
},
onFail: function (err) {
alert('error' + JSON.stringify(err));
}
})
},
function (err) {
alert('error' + JSON.stringify(err));
}
);
其中"/Api/DDUser/GetUserWithCode"是MVC下的DDUserController,其他的语言可以采取相似的方式实现,也可用一般处理程序来实现,我们的目的就是传入code,然后换取用户信息(该信息中包含钉钉用户的id),具体的可以参见官方的前面或者前面的【钉钉开发系列】文章。为了避免同一用户每次扫描时都去换取用户信息,可以将用户信息暂存起来,比如放到localstorage中,这样登录之后只需要判断是否已存在,若已存在则直接跳过不再发起请求。

4.qrlogin.html页面中确定并写入flag

在qrlogin.html页面中放一个确定按钮,点击后发起ajax请求,将key和钉钉用户的id一起传到服务器中,示例代码如下

$('#btn_qrcode_login_ok').live('click', function () {
try {
var userInfo = g.userInfo();
var key = $.getUrlParam('KEY');
g.toast.show('登录中...');
try {
$.getJSON("api/DDQRLogin/Confirm", 'key=' + key + '&dingDingUserId=' + userInfo.userid, function (response, textStatus, jqXHR) {
if (response.Status != g.StatusCode.OK) {
g.toast.hide();
alert('api/DDQRLogin/Confirm error.' + response.Message);
return;
}
var param = {};
param.title = '登录成功';
$('#btn_qrcode_login_ok').hide();
g.msg(param)
g.toast.hide();
});
} catch (e) {
alert("getjson exception:" + JSON.stringify(e.message));
g.toast.hide();
}
} catch (e) {
alert(e.message);
}
});
在后端收到key和dingDingUserId后,依据key将dingDingUserId写入到数据库。

5.二维码端以key向服务器查询flag

二维码端内部可以开起一个线程(thread或者task)向服务端查询是否已经写入了dingDingUserId,如果写入则说明已经扫描登录,这样UI就可以进行相应的跳转。
为了持续的查询dingDingUserId,一般有两种方式,第一种就是二维码端每隔一段时间就查询一次,也就是轮询。第二种就是二维码端向服务器查询后,服务器不立即返回,而是等待dingDingUserId写入到数据库中(这里可以采用以较小间隔查询数据库的方式来实现),一旦写入就立即返回给二维码端。
这两种方式各有利弊,
第一种,由于每次都是由二维码端发起请求,所以请求次数会明显增多,而且由于是查了之后再返回,所以响应会相对慢一些,但由于是请求就等待再请求,所以会相对的减小服务器的压力。
第二种,由于服务器端需要挂起等待dingDingUserId的写入,所以会一直占用服务器的资源,但拿到数据后会立即返回结果,响应会快一些。
具体应用也可考虑两者相结合的方式,比如二维码端隔一段合适的时间就发起请求,服务端挂起一段合适的时间,如果dingDingUserId还没有写入就先返回,以腾出资源给其他请求。
实际的效果如下图



欢迎打描左侧二维码打赏。

转载请注明出处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息