釘釘掃碼登陸第三方網站官方文檔索引:釘釘開放平臺 → (任意類型應用)→ 服務端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(); }); };