由于业务需求,因此需要对接钉钉的扫码登录功能,具体就是这边有一个旧系统,但是需要支持用户扫码登录,并且基于用户的身份(教师/学生, 或者 职员/管理者) 跳转到不同的页面, 这不是 so-easy 嘛. 然后就踩坑了
1. 文档有两个版本
懂得使用搜索引擎的人不难搜索到钉钉的开发文档,并且也不难看出 文档有两份,
旧文档的地址
新的文档的地址
那么问题来了,按那个文档来写呢? 答案是按旧的来写,别问,问就是后端是按旧的来写的 . 其实大概看一眼文档,就知道这两者大同小异,但是 前后端需要协调一致,按照同一份文档来写.
2.钉钉开发者管理后台也有新旧之分
钉钉的开发者后台也不难找,
新版管理后台
旧版管理后台
但是这里有个隐藏的坑是 这个管理界面有两个版本新版和旧版, 并且按我的实际使用体验来看,这两者 数据并不互通, 换句话说 旧版 维护的数据 和 新版 维护的数据是两份数据, 也不知是哪位高人?
我用的是旧版
3. 大概思路
期望的页面流程如下所示:
- 用户打开我们的登录页面,然后点一个钉钉登录的按钮,这时页面显示一个二维码
- 用户掏出手机扫码,然后选择确认登录,系统 loading,判断用户身份,然后跳转到指定页面
还是
so-easy
结合文档 ( 按照 旧版文档 方式二 ),这部分的具体思路就是,
- 首先用户打开我们的登录页
/login这个地址, - 点击
钉钉登录按钮, 页面此时显示钉钉二维码
4. 具体实现 (按照 旧版文档 方式二来实现)
注意目前是在登录页面,也就是一般的/login页面
- 如何显示二维码? 首先html 文件中引入 js
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
以及在 template 中增加相应 dom 节点 ,
<div id="login_container" class="self-defined-classname"></div>
以及加一个 按钮,点击该按钮之后, 二维码出现,该按钮的事件处理程序如下: 这里面的代码非常重要,也写了很多注释来解释
handleLogin(){
// 这是 钉钉开发者后台创建的应用中的appId , 并且我是用的旧版的 开发者后台
const appId = "dingxxxxxxxxxxxxxx";
// 这个地址需要注意,要和 钉钉开发者后台创建的应用中的回调域名保持一致, 这个地址在开发时可以这么写
const callbackUrl = "http://127.0.0.1:8080/#/loginRedirect";
var url = encodeURIComponent(callbackUrl);
// 这么拼接是没问题的,网上众说纷坛,实践上这么写没问题
let goto = encodeURIComponent(
`https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${appId}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${url}`
);
// 这里可以选择给 dingObj 赋值,不赋值应该也可以, 如果报错DDLogin未定义,使用 window.DDLogin
this.dingObj = DDLogin({
id: "login_container", // 和上面的 dom 的 id 保持一致
goto: goto,
style: "border:none;background-color:#FFFFFF;",
width: "365",
height: "400",
});
// 这个回调函数中可以写我们自己的一些处理逻辑,如何处理 loginTmpCode
var handleMessage = function (event) {
var origin = event.origin;
if (origin == "https://login.dingtalk.com") { //判断是否来自ddLogin扫码事件。
var loginTmpCode = event.data; //获取到loginTmpCode后就可以在这里构造跳转链接进行跳转了
// 这个 href 地址不能写错了,不然可能无法 302 重定向
console.log("loginTmpCode", loginTmpCode);
let href = `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${appId}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${url}&loginTmpCode=${loginTmpCode}`;
// 这里就是修改当前页面的 url,改成拼接而成的这个 url 的地址
// 执行之后,页面会302 重定向到 callbackUrl 这个地址,并且后面带有?code=24llfksdjflksjlfjs 这个参数
// 因此需要在前端页面中另外写一个页面,路由和callbackUrl一致,在这个页面中处理后续的逻辑,见下方
window.location.href = href;
}
};
if (typeof window.addEventListener != "undefined") {
window.addEventListener("message", handleMessage, false);
} else if (typeof window.attachEvent != "undefined") {
window.attachEvent("onmessage", handleMessage);
}
}
callbackUrl 对应的前端页面中的逻辑
这个页面由 /login 跳转而来,并且这页面的url 类似 callbackUrl + ?code=xxxxxxxxxxxxxxx&state=XXXX , 即 http://127.0.0.1:8080/#/loginRedirect?code=xxxxxxxx&state=XXXX
在这个页面中,则写一些我们自己的业务逻辑,例如根据身份跳转到不同页面,等等,大概例子如下
mounted() {
const { code } = this.$route.query;
console.log(44, code, this.$route);
if (code) {
//登录接口
dingdingLogin({ code: code })
.then((res) => {
const { userName: username, realPassword: password } = res.data;
const data = {
password,
username,
flag: "0", // 1 是正常登录, 0 是钉钉登录
code: "11", //验证码 此时随便写
};
this.$store
.dispatch("Login", data)
.then((data) => {
this.loading = false;
this.$router.push({ path: "/" }).catch(() => {});
})
.catch((err) => {});
})
.catch((err) => {});
}
},
appid 以及 回调域名在哪儿配置?
这个图里的回调域名应该是 http://127.0.0.1:8080/#/loginRedirect, 不过写错了 似乎也能用,我迷茫了
总结
总结一下就是 这个东西有坑啊,裂开,供大家参考,官方文档我感觉也未必准确,许多细节,文字不好讲清楚,还是要看代码