钉钉扫码登录实现过程
步骤如下:
1.设计好需要放钉钉扫码的页面
2.在钉钉上注册获取到生成相关二维码的页面链接URL,即请求相应接口获取到该页面链接:
<https://service.100tal.com/sso/login/236049354?uuid=d258113c-c39d-11e8-b304-5afc69781f14 >
注意: 这里的链接是由后端将钉钉返回的一个iframe标签窗口生成之后返回的,有可能后端会直接返回一个iframe标签,如果是直接跳转二维码可以不用生成链接,点击iframe可以直接跳转,如果生成二维码,则需要后端将iframe嵌套在某个URL页面里并返回改URL,然后再生成二维码
3.获取到链接后,去生成二维码,生成二维码需要安装qrcode插件,可以通过在命令行下载插件:
cnpm install -g qrcode
cnpm install -g jsonp
qrcode插件是用于生成二维码的,jsonp用来在页面内跨域请求
安装后会在package.json文件里出现对应插件
4.在组件里引入qrcodejs文件,然后在对应二维码代码中写qrcode组件,来生成二维码
<div id="qrcode" ref="qrcode" v-show="!isScanSuccess"></div>
import QRCode from 'qrcodejs2'
登录案例:
<div v-else>
<div class="qrcode-portion">
<div class="scan-success" v-show="isScanSuccess">
<p>扫码成功</p>
<p>请在手机钉钉中点击登录</p>
</div>
<div id="qrcode" ref="qrcode" v-show="!isScanSuccess"></div>
</div>
<div class="dd-login">请用钉钉扫码登录</div>
</div>
import QRCode from 'qrcodejs2'
生成二维码:
setQrcode () {
if (this.tokenTag) {
window.clearInterval(this.timer)
return
}
let qrcode = new QRCode('qrcode', {
width: 188,
height: 188,
text: this.qrcodeUrl, // 二维码地址
})
}
5.在service文件夹下写一个用于jsonp跨域的文件,在main.js里因为service。
注意:这里http.js跨域文件会单独发表一个博客文件
6.现在二维码已经显示了,然后使用后台返回的uuid去调用jsonp跨域请求钉钉,请求后会获取到一个token值,来判断你当前是否可以进行钉钉登录,主要是用来判断你是否在该公司名下,是否可以成功登录,即token为true,当token为true时调用登录接口和登录成功接口
waitSCan () {
const url = 'https://api.service.100tal.com/sso/qrcode/status'
this.$http.jsonp(url, {
uuid: this.uuid || ''
})
.then(res => {
if (res) {
this.getDingdingLogin(res)
} else {
console.log('jsonp请求失败')
}
})
.catch(err => {
window.clearInterval(this.qrcodeTimer) // 清除计时器
})
}
7.由于二维码会失效,所以需要1秒更新刷新一次二维码,保证二维码时刻有效
写到这里基本上就大功告成了,下面我会贴出钉钉二维码登录时的整体文件代码,使用时可以参考:
<template>
<div class="login">
<div class="login_form">
<img src="../../assets/img/logo-login.png" class='login_logo'/>
<div class="form_t">
<div class="login-tab clearfix">
<p :class="[tabTag == 'qrcode'? 'active' : '']" @click="clickTab('qrcode');showQrcode()">二维码登录</p>
<p :class="[tabTag == 'email'? 'active' : '']" @click="clickTab('email')">邮箱登录</p>
</div>
<div v-if="tabTag == 'email'">
<div class="user-name common-div">
<input type="text" name="userEmail" v-model="userEmail" placeholder="请输入邮箱地址"/>
<img src="../../assets/img/user.svg"/>
</div>
<div class="user-pasw common-div">
<input type="password" name="password" v-model="password" placeholder="请输入密码" @focus="cookiePassword" @keyup.enter="_login"/>
<img src="../../assets/img/lock.svg"/>
</div>
<div class="login-btn" @click="_login">登录</div>
<div class="footer clearfix">
<div class="remeber clearfix">
<span class="checkbox" @click='remeber'><i v-if='checked' class='fa fa-check'></i></span>
<label>记住密码</label>
</div>
<a class="forget" @click='showModal=true'>忘记用户名或密码?</a>
</div>
</div>
<div v-else>
<div class="qrcode-portion">
<div class="scan-success" v-show="isScanSuccess">
<p>扫码成功</p>
<p>请在手机钉钉中点击登录</p>
</div>
<div id="qrcode" ref="qrcode" v-show="!isScanSuccess"></div>
</div>
<div class="dd-login">请用钉钉扫码登录</div>
</div>
</div>
</div>
<modal :show.sync="showModal" class="modal-lg category_modal" :clean-data="cleanData" :click-confirm="clickConfirm">
<div slot="header">
找回密码
<span class="ico-close" @click='cleanData'>x</span>
</div>
<div slot="body" class="form-horizontal email">
<p>请输入您的100tal邮箱地址,然后前往重置密码</p>
<input type="text" id="" value="" v-model='email' placeholder="请输入邮箱"/>
</div>
</modal>
</div>
</template>
<script>
/* eslint-disable */
import { mapActions } from 'vuex'
import modal from '../../components/util/Modal'
import QRCode from 'qrcodejs2'
import { setTimeout } from 'timers';
export default {
data () {
return {
userEmail: '',
password: '',
showModal: false,
email: '',
checked: false,
tabTag: 'qrcode',
qrcodeUrl: '',
uuid: '',
scanOK: false, // 等待扫描结果
qrcodeTimer: null,
timer: null,
passStage: null,
tokenTag: false,
isScanSuccess: false
}
},
methods: {
...mapActions({
login: 'login',
getQrcode: 'getQrcode', //获取钉钉登录二维码
dingdingLogin: 'dingdingLogin', //钉钉登录
forgetPassword: 'forgetPassword', // 忘记密码或用户
setUserInfo: 'setUserInfo', // 存储用户信息
alertMsg: 'alertMsg'
}),
remeber () {
this.checked = !this.checked
},
setCookie(name, value, iDay) {
var oDate=new Date()
oDate.setDate(oDate.getDate()+iDay)
document.cookie=name+'='+encodeURIComponent(value)+';expires='+oDate
},
getCookie(name){
var arr = document.cookie.split('; ')
var i = 0
for(i=0; i<arr.length; i++){
var arr2 = arr[i].split('=')
if(arr2[0]==name){
var getC = decodeURIComponent(arr2[1])
return getC
}
}
return ''
},
cookiePassword() {
this.password = this.getCookie(this.userEmail)
},
_login () {
if (!this.userEmail || !this.password) {
this.alertMsg("请填写完整信息")
return
}
const params = {
email: this.userEmail,
password: this.password
}
this.setCookie('userName', this.userEmail)
if (this.checked) {
this.setCookie(this.userEmail, this.password,999);
}
this.login({
params: params,
setData: this.loginSuccess
})
},
loginSuccess (data) {
this.tokenTag = true
this.isScanSuccess = false
window.clearInterval(this.timer)
window.clearInterval(this.qrcodeTimer)
this.setUserInfo(data) // 存储用户信息
this.$router.replace('/handout')
},
showQrcode () {
if (this.tokenTag) return
let that = this
window.clearInterval(this.timer)
this.getQrcode({
params: '',
setData: this.setQrcodeToken
})
window.clearTimeout(this.passStage)
this.timer = setInterval(() => {
if (!that.$refs.qrcode) return
that.$refs.qrcode.innerHTML = ''
this.showQrcode()
}, 60000)
},
setQrcode () {
if (this.tokenTag) {
window.clearInterval(this.timer)
return
}
let qrcode = new QRCode('qrcode', {
width: 188,
height: 188,
text: this.qrcodeUrl, // 二维码地址
})
},
setQrcodeToken (data) {
window.clearInterval(this.qrcodeTimer) // 清除计时器
let that = this
let nextcall_eg = data.nextcall_eg
this.qrcodeUrl = data.qrcode
this.uuid = data.uuid
this.setQrcode()
this.waitSCan()
this.qrcodeTimer = setInterval(() => { this.waitSCan() }, 1000)
},
waitSCan () {
const url = 'https://api.service.100tal.com/sso/qrcode/status'
this.$http.jsonp(url, {
uuid: this.uuid || ''
})
.then(res => {
if (res) {
this.getDingdingLogin(res)
} else {
console.log('jsonp请求失败')
}
})
.catch(err => {
window.clearInterval(this.qrcodeTimer) // 清除计时器
})
},
getDingdingLogin (data) {
let that = this
if (data.stage == 9 && data.token){
this.tokenTag = true
const obj = {
"token": data.token
}
this.dingdingLogin({
params: obj,
setData: this.loginSuccess
})
}
if (data && data.errcode === 0 && data.stage === 1) {
this.isScanSuccess = true
this.passStage = setTimeout(() => {
if (!that.isScanSuccess) return
if (that.$refs.qrcode) that.$refs.qrcode.innerHTML = ''
that.showQrcode()
that.isScanSuccess = false
}, 61000) // 扫码未确认超出1分一秒时跳转二维码显示
}
},
cleanData () {
this.showModal = false
this.email = ''
},
clickConfirm () {
if (!this.email) {
this.alertMsg('邮箱不可以为空')
return
}
this.forgetPassword({
params: { email: this.email },
setData: () => {
this.alertMsg('发送成功,请前往邮箱查看'),
this.showModal = false
this.email = '';
},
erroCb: () => {
this.showModal = false
this.email = ''
}
})
},
clickTab (tabType) {
this.tabTag = tabType
}
},
mounted () {
this.showQrcode()
this.userEmail = this.getCookie('userName')
localStorage.removeItem('rembasicInfo')
if (this.userEmail) this.password = this.getCookie(this.userEmail)
},
components: {
modal
}
}
</script>
<style scoped>
.login{
min-height: 450px;
width: 312px;
position: absolute;
top:50%;
left: 50%;
margin-left: -156px;
margin-top: 129px;
}
.form_t{
height: 320px;
background: rgba(0,0,0,0.30);
border-radius: 6px;
padding: 40px;
}
.form_t div {
width: 232px;
height: 36px;
line-height: 36px;
border-radius: 3px;
}
.form_t .login-tab {
height: 34px;
background:rgba(255,255,255,0);
border:1px solid rgba(255,255,255,0.24);
border-radius: 18px;
color:rgba(255,255,255,1);
margin-bottom: 36px;
font-family:PingFangSC-Regular;
}
.form_t .login-tab .active {
background: rgba(255,255,255,0.23);
}
.login-tab p {
width: 115px;
height: inherit;
float: left;
cursor: pointer;
}
.login-tab p:first-child {
border-right: 1px solid rgba(255,255,255,0.24);
border-radius:18px 0px 0px 18px;
}
.login-tab p:last-child {
width: 116px;
border-radius:0 18px 18px 0;
}
.txt{
height:112px;
line-height: 112px;
font-size: 18px;
color: #fff;
}
.common-div{
position:relative;
}
.common-div input{
width: calc(100% - 9px);
height: calc(100% - 2px);
font-size: 14px;
padding-left:8px ;
color: #fff;
border:0;
border-radius: 3px;
background-color: rgba(0,0,0,0.24);
}
.common-div input::placeholder {
color: #fff;
}
.user-pasw {
margin: 24px 0 48px 0;
}
.common-div img{
display: block;
width: 14px;
height: 14px;
position: absolute;
right: 10px;
top: 11px;
}
.footer {
position: relative;
}
.form_t .remeber{
float: left;
width: 68px;
color: #fff;
font-size: 12px;
height: 12px;
line-height: 14px;
margin-top: 12px;
}
.checkbox{
float: left;
width: 12px;
height: 12px;
border: 1px solid #BDBDBD;
background: #fff;
border-radius: 3px;
margin-right: 5px;
}
.login-btn{
line-height: 40px;
font-size: 20px;
color: #fff;
height:40px;
background:rgba(245,166,35,0.5);
border-radius:3px;
border:1px solid rgba(245,166,35,0.8);
cursor: pointer;
font-family:PingFangSC-Regular;
}
.login_logo{
margin-bottom: 24px;
width: 318px;
height: 42px;
}
.forget{
float: right;
color: #fff;
cursor: pointer;
border-bottom: 1px solid #fff;
height: 24px;
font-size: 12px;
}
.email p{
padding-top:50px ;
width: 400px;
height: 40px;
line-height: 40px;
text-align: center;
font-size:16px ;
font-weight: 700;
}
.email input{
display: block;
width: 350px;
height: 40px;
line-height: 40px;
padding-left: 14px;
border: 1px solid #979797;
box-shadow: inset 0 1px 3px 0 rgba(0,0,0,.5);
border-radius: 5px;
color: #949494;
margin-top:20px;
margin-left: 24px;
}
.fa-check{
color: #000;
}
.form_t .qrcode-portion {
margin-top: 42px;
width: calc(100% - 32px);
height: 200px;
background:rgba(255,255,255,0.23);
box-shadow:0px 0px 1px 0px rgba(0,0,0,0.5),0px 0px 1px 0px rgba(255,255,255,0.5);
border-radius:6px;
padding: 16px;
}
.form_t .qrcode-portion .scan-success{
width: 100%;
height: auto;
margin-top: 61px;
color: rgba(0,0,0,.5);
}
.scan-success p:first-child{
color: #48c55f;
font-size: 20px;
font-weight: 500;
}
.form_t .dd-login {
color:rgba(255,255,255,1);
margin-top: 12px;
height: 14px;
line-height: 14px;
}
#qrcode {
display: inline-block;
width: calc(100% - 12px);
height: calc(100% - 12px);
padding: 6px;
background-color: #fff;
}
</style>