采用OAuth2.0
1.template中添加点击事件
<div class="google" @click="thirdLogin('GOOGLE')"/>
接口请求时loading效果
<div
v-if="loadingThird"
class="dialog-layout flex justify-center align-center"
>
<b-spinner
variant="primary"
style="width: 3rem; height: 3rem"
></b-spinner>
</div>
2.跳转第三方登录页
// type为当前第三方平台类型 WX|GOOGLE
async thirdLogin(type) {
// 获取第三方登录相关参数,如appId等...
await this.getThirdConfig()
// 可自行在本地配置,例如
const googleConfig = {
client_id: 'xxx', // 客户端ID
redirect_uri: encodeURIComponent('xxx'), // 跳转回调地址,要进行url编码
response_type: 'xxx', // 谷歌设置为token
scope: 'https://www.googleapis.com/auth/userinfo.email', // 得到谷歌的用户信息
state: 'xxx', // 防攻击,可配置成随机数等....
}
localStorage.setItem('thirdLogin', type)
switch (type) {
case 'WX':
this.$refs.wxLogin.openDialog(this.wechatLogin) // 微信封装成了组件
break
case 'GOOGLE':
url = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${
this.googleLogin.clientid
}&redirect_uri=${
encodeURIComponent(this.googleLogin.redirectUri)
}&response_type=${
OAuth.google.type
}&scope=${
this.googleLogin.scope
}&state=${
this.googleLogin.state
}`
window.open(url, '_blank') // 跳转谷歌登录
break
}
}
3.通过回调redirect_uri的地址后面的code字段请求第三方接口返回access_token
谷歌设置response_type为token时,将直接在回调地址后通过hash的方式返回access_token等信息
获取到谷歌的access_token及微信的code时要做持久化处理,保证在绑定页面刷新时接口正常请求
保存在localStorage的第三方信息通过crypto-js加密
// 第三方登录获取code
async getThirdCode() {
let currentThird = localStorage.getItem('thirdLogin')
let type = null
let { hash, query } = this.$route
this.code = query.code
if(query.code) {
const encryptCode = encrypt(query.code)
localStorage.setItem('WX_CODE', encryptCode)
}
// 谷歌登录返回hash地址
if (hash) {
// accessToken处理, 提取出access_token并将结尾的&token_type字符去掉
this.accessToken = hash.split('=')[2]?.replace('&token_type', '')
const accessToken = encrypt(this.accessToken)
localStorage.setItem('GOOGLE', accessToken)
}
// 谷歌第三方
if(currentThird === 'GOOGLE') {
// 登录接口
type = 'GOOGLE'
this.postThird(type)
} else if (currentThird === 'WX') { // 微信第三方
const params = {
code: decrypt(localStorage.getItem('WX_CODE'))
}
const accessRes = await getWechatToken(this, params)
console.log('获取微信accessToken:', accessRes);
if(accessRes && accessRes.code == 0) {
type = 'WX'
this.accessToken = accessRes.data.data?.access_token ?? null
this.openId = accessRes.data.data?.openid ?? null
// 加密
const accessToken = encrypt(this.accessToken)
const openId = encrypt(this.openId)
const obj = {
accessToken,
openId
}
if(!accessRes.data.data.errcode) {
localStorage.setItem('WX', JSON.stringify(obj))
}
this.postThird(type)
}
}
}
4.将access_token传递给后端,返回当前官网用户的token,进行登录或者绑定操作
access_token包含第三方登录用户的信息。因为我这里后端获取用户token有两种响应方式,所以代码较为繁杂,可根据具体情况进行修改。
// 第三方跳转后官网登录接口
async postThird(type) {
let params;
switch(type) {
case 'WX':
console.log('微信登录');
params = {
accessToken: decrypt(JSON.parse(localStorage.getItem('WX')).accessToken),
openId: decrypt(JSON.parse(localStorage.getItem('WX')).openId)
}
break
case 'GOOGLE':
console.log('谷歌登录');
params = {
accessToken: decrypt(localStorage.getItem('GOOGLE')),
}
}
// 获取用户token
const loginRes = await getThirdLogin(this, params , type)
this.loadingThird = true
if(loginRes.code == 0) {
// loginRes会有两种返回形式,
// 1. { code: 0, data: 'token' } 未绑定
// 2. { code: 0, data: { code: 'SUCCESS', data: {}, message } } 已绑定
if(loginRes.data && typeof (loginRes.data) == 'string') {
// 设置token
// 未绑定跳转;
this.$store.dispatch('user/login', loginRes.data)
this.loadingThird = false
this.$router.push({
path: this.$i18n.path('/binding'),
})
}
// 已绑定返回
let res = loginRes.data.data
// 设置token
this.$store.dispatch('user/login', res.token)
this.loadingThird = false
const userinfo = await this.getUser()
this.$store.dispatch('user/saveUser', userinfo)
// 已绑定删除缓存;
localStorage.removeItem('thirdLogin')
localStorage.removeItem('WX')
localStorage.removeItem('GOOGLE')
localStorage.removeItem('WX_CODE')
this.$router.push({
path: this.$i18n.path('/')
})
} else {
this.loadingThird = false
localStorage.removeItem('thirdLogin')
this.$message(
this,
loginRes.message || loginRes.data.data.message,
'danger'
)
console.log('登录出错:', loginRes, params);
}
},
5.未绑定用户跳转至绑定页面
从绑定页面离开时要清除所有有关的localStorage -> thirdLogin,WX,GOOGLE,WX_CODE
使用vue-router组件内守卫
beforeRouteLeave(to, from, next) {
localStorage.removeItem('thirdLogin')
localStorage.removeItem('WX')
localStorage.removeItem('GOOGLE')
localStorage.removeItem('WX_CODE')
next()
},
<b-button
type="submit"
variant="primary"
class="submit radius-12"
@click="thirdLoginBtn"
>
{{ $t('login.bind_btn') }}
</b-button>
// 绑定页面
async thirdLoginBtn() {
// 校验
const [flag, params] = this.validateBindForm()
if(flag) {
const BindRes = await thirdBindUser(this, params)
console.log('绑定用户:', BindRes)
if (BindRes && BindRes.code == 0) {
let type = localStorage.getItem('thirdLogin')
let data;
switch(type) {
case 'WX':
console.log('绑定微信登录');
data = {
accessToken: decrypt(JSON.parse(localStorage.getItem('WX')).accessToken),
openId: decrypt(JSON.parse(localStorage.getItem('WX')).openId)
}
break
case 'GOOGLE':
console.log('绑定谷歌登录');
data = {
accessToken: decrypt(localStorage.getItem('GOOGLE')),
}
}
const loginRes = await getThirdLogin(this, data , type)
console.log('绑定用户登录:', loginRes);
if(loginRes.code == 0) {
// 设置token
this.$store.dispatch('user/login', loginRes.data.data.token)
const userinfo = await this.getUser()
this.$store.dispatch('user/saveUser', userinfo)
// 删除缓存(绑定用户登录成功);
localStorage.removeItem('thirdLogin')
localStorage.removeItem('WX')
localStorage.removeItem('GOOGLE')
localStorage.removeItem('WX_CODE')
this.$router.push({
path:this.$i18n.path('/')
})
} else {
// 删除缓存(绑定用户登录失败);
localStorage.removeItem('thirdLogin')
localStorage.removeItem('WX')
localStorage.removeItem('GOOGLE')
localStorage.removeItem('WX_CODE')
// 登陆失败
this.$message(
this,
loginRes.data.data.message,
'danger'
)
}
} else {
// 绑定失败
this.$message(
this,
BindRes.message || BindRes.data.data.message,
'danger'
)
}
}
},