钉钉扫码登陆第三方网站官方文档索引:钉钉开放平台 → (任意类型应用)→ 服务端API → 身份验证 → 扫码登陆第三方网站,文档中详细的介绍了钉钉扫码登陆功能的实现。javascript
因为表单复杂,用户编辑时间过长,提交表单提示token过时失效,须要从新扫码登陆,然而用户不但愿跳到登陆页,更不但愿从新编辑表单。css
一、token失效,在当前页面弹出登陆二维码。html
二、若是原用户扫码登陆成功可继续编辑表单。java
三、若是非原用户扫码登录成功则更新权限与功能并跳转首页。ios
官方扫码登陆后须要页面跳转来传递 loginTmpCode
值,因此不能使用扫码登陆第三方网站功能登陆,须要自定义扫码登陆功能。web
开发前准备:axios
一、应用开发 → 建立H5微应用:免登鉴权。c#
二、登陆接口:鉴权后获取用户信息。api
三、H5微应用页面:钉钉端鉴权与登陆功能。session
四、发起登陆的接口:用于发起一个登陆,返回登陆id。
五、获取登陆的接口:用于轮询获取登陆状态,返回指定id的登陆的状态与信息。
六、设置登陆状态接口:设置用户登陆状态与信息。
七、安装 qrcode 模块:用来生成登陆二维码。
如下为实际项目的实现,代码有所调整。
一、判断token失效,备份原用户信息。
// 请求拦截器 import request from 'axios'; import Bus from './bus'; const resetUser = (urlRedirect, code) => { let user = sessionStorage.getItem('user'); if (user) { sessionStorage.setItem('userResign', user); if (code === '105') { removeStorage('user'); } removeStorage('token'); Bus.$emit('userResign'); } else { if (typeof urlRedirect === 'string' && urlRedirect !== '') { location.href = urlRedirect; } } }; let instance = request.create(); instance.interceptors.response.use((response) => { let urlRedirect = '/signIn'; if (response.data && response.data.code) { if (response.data.code === '101') { alert('登陆超时,请从新登陆'); resetUser(urlRedirect, response.data.code); } // ... } return response; }, (error) => { return Promise.reject(error); });
二、经过生成登陆的接口建立登陆,得到登陆id,生成微应用二维码,并传递登陆id。
data() { return { icons: { refresh: '刷新图标' }, userResign: !1, signStep: '', signSrc: '' }; }, methods: { createResign() { return this.$axios.get('建立登陆接口'); }, getSignInfo(signId) { return this.$axios.get('获取登陆接口', { params: { signId } }); }, async getSignState(signId) { if (!signId) { return; } let { data } = await this.getSignInfo(signId); let { step } = data; // 设置状态 // "resignCreated" 建立登陆id和访问url // "resignScanned" 用户扫码完成 // "resignSuccess" 登陆成功 // "resignTimeout" 执行超时 this.signStep = step; if (step === 'resignCreated' || step === 'resignScanned') { clearTimeout(this.timerResign); this.timerResign = setTimeout(() => { this.getSignState(signId); }, 5000); } if (step === 'resignSuccess') { let user = JSON.parse(sessionStorage.getItem('userResign')); if (data.accessToken) { // ... sessionStorage.removeItem('userResign'); sessionStorage.setItem('token', data.accessToken); sessionStorage.setItem('user', JSON.stringify(data.user)); if (user.userid !== data.user.userid) { alert('切换登陆成功'); location.href = '/'; } else { alert('从新登陆成功'); this.userResign = !1; } } } }, async resign() { clearTimeout(this.timerResign); this.signStep = ''; // 建立登陆返回登陆id,登陆时效一分钟 let { data: { signId } } = await this.createResign(); // 建立成功后,该登陆时效内轮询获取登陆状态 signId && this.getSignState(signId); let uri = `微应用地址?signId=${ signId }`; // 生成微应用地址二维码 this.signSrc = await QRCode.toDataURL(uri, { width: 210, height: 210, margin: 0 }); } }, created() { // 用户身份过时 this.$bus.$on('userResign', () => { this.userResign = !0; this.$nextTick(this.resign); }); }
<!-- 登陆窗口 --> <div class="userResign" v-if="userResign"> <div class="viewUserResign"> <div class="viewHead"> 从新登陆 </div> <div class="viewContainer"> <div class="viewSign"> <div class="wxCode-box"> <div class="signImg"><img :src="signSrc" alt="" v-if="signSrc" /></div> <div class="signRefresh" v-show="signStep === 'resignTimeout'">您的二维码已失效,<br>请点击下方刷新按钮</div> <div class="wxCode-text" v-if="signStep === 'resignScanned'">二维码已扫描</div> <div class="wxCode-text" v-else>请使用钉钉扫描二维码登陆 <span @click.stop="resign"><i v-html="icons.refresh"></i>刷新</span></div> </div> </div> </div> </div> </div>
/* userResign */ .userResign { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 10000; background-color: rgba(255, 255, 255, .5); .viewUserResign { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 10000; width: 658px; height: 436px; margin: auto; background-color: #252525; border-radius: 4px; overflow: hidden; .viewHead { height: 40px; border-bottom: 2px solid #424242; line-height: 40px; color: #ffffff; font-size: 18px; font-weight: normal; padding: 0 12px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .viewContainer { padding: 14px; } .viewSign { padding: 20px 0; background-color: #434343; border-radius: 4px; } .wxCode-box { position: relative; width: 300px; height: 326px; margin: 0 auto; padding: 40px 0; background-color: #ffffff; border-radius: 4px; box-sizing: border-box; .signImg { display: block; width: 210px; height: 210px; background-color: #ffffff; margin: 0 auto; } .signRefresh { position: absolute; top: 40px; left: 45px; width: 210px; height: 210px; padding-top: 90px; background-color: rgba(255, 255, 255, 0.9); text-align: center; color: #fa5b5b; line-height: 1.4; box-sizing: border-box; } } .wxCode-text { display: flex; justify-content: center; align-items: center; margin-top: 20px; text-align: center; color: #898d90; span { display: flex; align-items: center; margin-left: 5px; color: #38adff; cursor: pointer; svg { display: block; margin-right: 1px; } } } } }
三、钉钉扫码后访问微应用,接收登陆id,经过钉钉JSAPI鉴权。
var userId = ''; var signId = '获取登陆id'; var signIn = function(authCode) { dd.device.notification.showPreloader({ text: '正在加载中..', showIcon: true }); this.$axios .get('登陆接口?authCode=' + authCode) .then(function (result) { // ... userId = result.user.userid; dd.device.notification.hidePreloader(); }) .catch(function(err) { dd.device.notification.hidePreloader(); }); }; var ready = function () { dd.runtime.permission.requestAuthCode({ corpId: '公司corpId', onSuccess: function (result) { result.code && signIn(result.code); }, onFail: function (err) { } }); }; if (dd.env.platform === 'notInDingTalk') { problem('请在钉钉内打开该应用'); } else { dd.ready(ready); dd.error(function (error) { alert('dd error: ' + JSON.stringify(error)); }); } // 点击页面中登陆按钮,设置登陆状态 // PC端轮询得到登陆状态为 var setSignState = function () { dd.device.notification.showPreloader({ text: '正在加载中..', showIcon: true }); this.$axios .get('设置登陆接口', { params: { signId: signId, userid: userId } }) .then(function(result) { d.device.notification.hidePreloader(); alert(result.msg || '登陆成功!'); dd.biz.navigation.close(); }) .catch(function(err) { dd.device.notification.hidePreloader(); }); };