主要實現(xiàn)步驟
- 對接第三方平臺,獲取第三方平臺的用戶信息。
- 利用該用戶信息,完成本應(yīng)用的注冊。
qq登錄接入
接入前的配置
登錄后,點擊頭像,進(jìn)行開發(fā)者信息填寫,等待審核。
郵箱驗證后,等待審核。
審核通過后,然后就可以創(chuàng)建應(yīng)用了。
然后填寫一些網(wǎng)站信息,等待審核。審核通過后,即可使用。
開始接入
1.導(dǎo)入qq登錄的sdk
<!-- QQ 登錄 -->
<script type="text/javascript" charset="utf-8" src="https://connect.qq.com/qc_jssdk.js" data-appid="您應(yīng)用的appid"
data-redirecturi="qq掃碼后的回調(diào)地址(上面配置中可以查到)"></script>
2.點擊qq登錄,彈出掃碼窗口。
// QQ 登錄的 URL
const QQ_LOGIN_URL =
'https://graph.qq.com/oauth2.0/authorize?client_id=您的appid&response_type=token&scope=all&redirect_uri=您的掃碼后的回調(diào)地址'
window.open(
QQ_LOGIN_URL,
'oauth2Login_10609',
'height=525,width=585, toolbar=no, menubar=no, scrollbars=no, status=no, location=yes, resizable=yes'
)
3.掛起qq登錄。需要注意的是,掃碼登錄成功后,調(diào)試代碼需要在線上環(huán)境。
<span id="qqLoginBtn" v-show="false"></span>
// QQ 登錄掛起
onMounted(() => {
QC.Login(
{
btnId: 'qqLoginBtn' //插入按鈕的節(jié)點id
},
// 登錄成功之后的回調(diào),但是需要注意,這個回調(diào)只會在《登錄回調(diào)頁面中被執(zhí)行》
// 登錄存在緩存,登錄成功一次之后,下次進(jìn)入會自動重新登錄(即:觸發(fā)該方法,所以我們應(yīng)該在離開登錄頁面時,注銷登錄)
// data就是當(dāng)前qq的詳細(xì)信息
(data, opts) => {
console.log('QQ登錄成功')
// 1. 注銷登錄,否則在后續(xù)登錄中會直接觸發(fā)該回調(diào)
QC.Login.signOut()
// 2. 獲取當(dāng)前用戶唯一標(biāo)識,作為判斷用戶是否已注冊的依據(jù)。(來決定是否跳轉(zhuǎn)到注冊頁面)
const accessToken = /access_token=((.*))&expires_in/.exec(
window.location.hash
)[1]
// 3. 拼接請求對象
const oauthObj = {
nickname: data.nickname,
figureurl_qq_2: data.figureurl_qq_2,
accessToken
}
// 4. 完成跨頁面?zhèn)鬏?(需要將數(shù)據(jù)傳遞到項目頁面,而非qq登錄彈框頁面中進(jìn)行操作)
brodacast.send(oauthObj)
// 針對于 移動端而言:通過移動端觸發(fā) QQ 登錄會展示三個頁面,原頁面、QQ 吊起頁面、回調(diào)頁面。并且移動端一個頁面展示整屏內(nèi)容,且無法直接通過 window.close() 關(guān)閉,所以在移動端中,我們需要在當(dāng)前頁面繼續(xù)進(jìn)行后續(xù)操作。
oauthLogin(LOGIN_TYPE_QQ, oauthObj)
// 5. 在 PC 端下,關(guān)閉第三方窗口
window.close()
}
)
})
4.跨頁面窗口通信
想要實現(xiàn)跨頁面信息傳輸,通常有兩種方式:
- BroadcastChannel:允許 同源 的不同瀏覽器窗口,Tab頁,frame或者 iframe 下的不同文檔之間相互通信。但是會存在兼容性問題。
- localStorage + window.onstorage:通過localStorage 進(jìn)行 同源 的數(shù)據(jù)傳輸。用來處理 BroadcastChannel 不兼容的瀏覽器。
// brodacast.js
// 頻道名
const LOGIN_SUCCESS_CHANNEL = 'LOGIN_SUCCESS_CHANNEL'
// [email protected] 不支持 BroadcastChannel,所以我們需要對其進(jìn)行判定使用,在不支持 BroadcastChannel 的瀏覽器中,使用 localstorage
let broadcastChannel = null
if (window.BroadcastChannel) {
broadcastChannel = new BroadcastChannel(LOGIN_SUCCESS_CHANNEL)
}
/**
* 等待 QQ 登錄成功
* 因為 QQ 登錄會在一個新的窗口中進(jìn)行,用戶掃碼登錄成功之后會回調(diào)《新窗口的 QC.Login 第二參數(shù) cb》,而不會回調(diào)到原頁面。
* 所以我們需要在《新窗口中通知到原頁面》,所以就需要涉及到 JS 的跨頁面通訊,而跨頁面通訊指的主要就是《同源頁面的通訊》
* 同源頁面的通訊方式有很多,我們這里主要介紹:
* 1. BroadcastChannel -> https://developer.mozilla.org/zh-CN/docs/Web/API/BroadcastChannel
* 2. window.onstorage:注意:該事件不在導(dǎo)致數(shù)據(jù)變化的當(dāng)前頁面觸發(fā)
*/
/**
* 等待回調(diào),它將返回一個 promise,并攜帶對應(yīng)的數(shù)據(jù)
*/
const wait = () => {
return new Promise((resolve, reject) => {
if (broadcastChannel) {
// 觸發(fā) message 事件時的回調(diào)函數(shù)
broadcastChannel.onmessage = async (event) => {
// 改變 promise 狀態(tài)
resolve(event.data)
}
} else {
// 觸發(fā) localStorage 的 setItem 事件時回調(diào)函數(shù)
window.onstorage = (e) => {
// 判斷當(dāng)前的事件名
if (e.key === LOGIN_SUCCESS_CHANNEL) {
// 改變 promise 狀態(tài)
resolve(JSON.parse(e.newValue))
}
}
}
})
}
/**
* 發(fā)送消息。
* broadcastChannel:觸發(fā) message
* localStorage:觸發(fā) setItem
*/
const send = (data) => {
if (broadcastChannel) {
broadcastChannel.postMessage(data)
} else {
localStorage.setItem(LOGIN_SUCCESS_CHANNEL, JSON.stringify(data))
}
}
/**
* 清除
*/
const clear = () => {
if (broadcastChannel) {
broadcastChannel.close()
broadcastChannel = null
}
localStorage.removeItem(LOGIN_SUCCESS_CHANNEL)
}
export default {
wait,
send,
clear
}
5.拿到數(shù)據(jù)后,進(jìn)行登錄(自己服務(wù)器登錄接口)操作。
- 傳入對應(yīng)參數(shù)(loginType, accessToken)等參數(shù)進(jìn)行用戶注冊判斷。
- 通過accessToken判斷用戶已經(jīng)注冊,那么我們就直接在后臺查出用戶名和密碼直接登錄了。
- 通過accessToken判斷用戶未注冊,那么我們將跳轉(zhuǎn)到注冊頁面,讓其注冊。
// 打開視窗之后開始等待
brodacast.wait().then(async (oauthObj) => {
// 登錄成功,關(guān)閉通知
brodacast.clear()
// TODO: 執(zhí)行登錄操作
oauthLogin("QQ", oauthObj)
})
// oauthLogin.js
import store from '@/store'
import router from '@/router'
import { message } from '@/libs'
import { LOGIN_TYPE_OAUTH_NO_REGISTER_CODE } from '@/constants'
/**
* 第三方登錄統(tǒng)一處理方法
* @param {*} oauthType 登錄方式
* @param {*} oauthData 第三方數(shù)據(jù)
*/
export const oauthLogin = async (oauthType, oauthData) => {
const code = await store.dispatch('user/login', {
loginType: oauthType,
...oauthData
})
// 返回 204 表示當(dāng)前用戶未注冊,此時給用戶一個提示,走注冊頁面
if (code === LOGIN_TYPE_OAUTH_NO_REGISTER_CODE) {
message('success', `歡迎您 ${oauthData.nickname},請創(chuàng)建您的賬號`, 6000)
// 進(jìn)入注冊頁面,同時攜帶當(dāng)前的第三方數(shù)據(jù)和注冊標(biāo)記
router.push({
path: '/register',
query: {
reqType: oauthType,
...oauthData
}
})
return
}
// 否則表示用戶已注冊,直接進(jìn)入首頁
router.push('/')
}
微信掃碼登錄接入
登錄后,進(jìn)行對應(yīng)的應(yīng)用注冊,填寫一大堆詳細(xì)信息,然后進(jìn)行交錢,就可以使用微信登錄了。
開始接入
整個微信登錄流程與QQ登錄流程略有不同,分為以下幾步:
1.通過 微信登錄前置數(shù)據(jù)獲取 接口,獲取登錄數(shù)據(jù)(比如 APP ID)。就是后臺將一些敏感數(shù)據(jù)通過接口返回。
2.根據(jù)獲取到的數(shù)據(jù),拼接得到 open url 地址。打開該地址,展示微信登錄二維碼。移動端微信掃碼確定登錄。
// 2. 根據(jù)獲取到的數(shù)據(jù),拼接得到 `open url` 地址
window.open(
`https://open.weixin.qq.com/connect/qrconnect?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`,
'',
'height=525,width=585, toolbar=no, menubar=no, scrollbars=no, status=no, location=yes, resizable=yes'
)
3.等待用戶掃碼后,從當(dāng)前窗口中解析 window.location.search
得到用戶的 code
數(shù)據(jù)。 微信掃碼后,會重定向到登錄頁面。
/**
* 微信登錄成功之后的窗口數(shù)據(jù)解析
*/
if (window.location.search) {
const code = /code=((.*))&state/.exec(window.location.search)[1]
if (code) {
brodacast.send({
code
})
// 關(guān)閉回調(diào)網(wǎng)頁
window.close()
}
}
4.根據(jù) appId、appSecret、code 通過接口獲取用戶的 access_token
5.根據(jù) access_token 獲取用戶信息
6.通過用戶信息觸發(fā) oauthLogin 方法。
調(diào)用的接口,都是后端通過微信提供的api來獲取到對應(yīng)的數(shù)據(jù),然后再通過接口返回給開發(fā)者。
// 等待掃碼登錄成功通知
brodacast.wait().then(async ({ code }) => {
console.log('微信掃碼登錄成功')
console.log(code)
// 微信登錄成功,關(guān)閉通知
brodacast.clear()
// 獲取 AccessToken 和 openid
const { access_token, openid } = await getWXLoginToken(
appId,
appSecret,
code
)
// 獲取登錄用戶信息
const { nickname, headimgurl } = await getWXLoginUserInfo(
access_token,
openid
)
console.log(nickname, headimgurl)
// 執(zhí)行登錄操作
oauthLogin(LOGIN_TYPE_WX, {
openid,
nickname,
headimgurl
})
})
需要注意的是,在手機(jī)端,普通h5頁面是不能使用微信掃碼登錄的。
總結(jié)
相同點
- 接入前需要配置一些內(nèi)容信息。
- 都需要在線上環(huán)境進(jìn)行調(diào)試。
- 都是掃碼后在三方窗口中獲取對應(yīng)的信息,發(fā)送到當(dāng)前項目頁面進(jìn)行請求,判斷用戶是否已經(jīng)注冊,還是未注冊。已經(jīng)注冊時,調(diào)用login接口時,password直接傳遞空字符串即可,后端可以通過唯一標(biāo)識,獲取到對應(yīng)的用戶名和密碼,直接返回token進(jìn)行登錄。未注冊,就跳轉(zhuǎn)到注冊頁面,讓其注冊。
不同點
- qq接入需要導(dǎo)入qc_sdk。
- qq直接掃碼后即可獲取到用戶信息,就可以直接調(diào)用login接口進(jìn)行判斷用戶是否注冊了。
- 微信掃碼后,獲取
code
來換取access_token, openid
,然后再通過access_token, openid
來換取用戶信息。然后再調(diào)用login接口進(jìn)行判斷用戶是否注冊了。 - 作者:Spirited_Away