博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
钉钉开发系列(八)二维码扫描登录的实现
阅读量:4881 次
发布时间:2019-06-11

本文共 6134 字,大约阅读时间需要 20 分钟。

钉钉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) {            /// 配置微信的JSSDK            /// 要请求的api列表,以['biz.chat.chooseConversationByCorpId','biz.chat.toConversation']的形式            /// dd.ready调用的函数            /// dd.error调用的函数              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还没有写入就先返回,以腾出资源给其他请求。
实际的效果如下图

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

转载请注明出处。

转载于:https://www.cnblogs.com/sparkleDai/p/7604923.html

你可能感兴趣的文章
virtualenv和virtualenvwrapper 的安装和使用
查看>>
MAC sublime text 无法自动补齐标签
查看>>
NgBook留言本开发全过程(1)
查看>>
LeetCode-指针法
查看>>
Mysql phpStudy升级Mysql版本,流产了怎么办?
查看>>
SQLServer之数据库行锁
查看>>
OFDM仿真
查看>>
浅谈linux内核中内存分配函数
查看>>
写在读研初期
查看>>
开环增益对负反馈放大电路的影响
查看>>
MySQL-ERROR 2003
查看>>
SQL Server2012-SSIS的包管理和部署
查看>>
JavaScript内置对象
查看>>
如何把js的循环写成异步的
查看>>
ER图是啥?
查看>>
too many include files depth = 1024错误原因
查看>>
HTTP协议详解(三)
查看>>
Android零基础入门第84节:引入Fragment原来是这么回事
查看>>
解析SQL Server之任务调度
查看>>
参考资料地址
查看>>