钉钉扫码登录成功禁止跳转刷新

蛋炒太阳

钉钉扫码登录第三方网站官方文档索引:钉钉开放平台 → (任意类型应用)→ 服务端API → 身份验证 → 扫码登录第三方网站,文档中详细的介绍了钉钉扫码登录功能的实现。

以下是大致的实现思路,对比实际项目代码有所改动和删减,具体细节不清楚的地方可以留言提问。

出现需求

由于表单复杂,用户编辑时间过长,提交表单提示token过期失效,需要重新扫码登录,然而用户不希望跳到登录页,更不希望重新编辑表单。

1、token失效,在当前页面弹出登录二维码。

2、如果原用户扫码登录成功可继续编辑表单。

3、如果非原用户扫码登陆成功则更新权限与功能并跳转首页。

解决需求

官方扫码登录后需要页面跳转来传递 loginTmpCode 值,所以不能使用扫码登录第三方网站功能登录,需要自定义扫码登录功能。

开发前准备:

1、应用开发 → 创建H5微应用:免登鉴权。

2、登录接口:鉴权后获取用户信息。

3、H5微应用页面:钉钉端鉴权与登录功能。

4、发起登录的接口:用于发起一个登录,返回登录id。

5、获取登录的接口:用于轮询获取登录状态,返回指定id的登录的状态与信息。

6、设置登录状态接口:设置用户登录状态与信息。

7、安装 qrcode 模块:用来生成登录二维码。

上码

以下为实际项目的实现,代码有所调整。

1、判断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);
});

2、通过生成登录的接口创建登录,获得登录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;
                }
            }
        }
    }
}

3、钉钉扫码后访问微应用,接收登录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();
        });
};

更新

测试后发现了一些问题:

Q:登录超时,浏览器刷新,依然弹出重新登录二维码,实际需要跳转到登录页。
A:增加判断条件,浏览器加载会重置该条件,从而判断跳转与弹出。

阅读 4.5k

前端开发
前端开发
321 声望
6 粉丝
0 条评论
你知道吗?

321 声望
6 粉丝
文章目录
宣传栏