前言
微信授權(quán)登錄是微信公眾號開發(fā)中繞不開的話題,整個(gè)授權(quán)登錄流程的實(shí)現(xiàn)需要前后端的配合。以前前后端不分離的時(shí)候微信狼王授權(quán)碼注冊機(jī),也許我們的前端不需要太關(guān)心授權(quán)的具體實(shí)現(xiàn)。不過現(xiàn)在是2021年,前后端分離的架構(gòu)非常流行。如何在前后端分離的情況下實(shí)現(xiàn)微信授權(quán)登錄成為今天要討論的重點(diǎn)問題。
準(zhǔn)備
首先,我們還是要梳理一下微信授權(quán)的全流程。這里我直接搬官方文檔:
如果用戶在微信客戶端訪問第三方網(wǎng)頁,公眾號可以通過微信網(wǎng)頁授權(quán)機(jī)制獲取用戶的基本信息,進(jìn)而實(shí)現(xiàn)業(yè)務(wù)邏輯。
…
關(guān)于兩種網(wǎng)頁授權(quán)范圍的區(qū)別:
1、scope發(fā)起的網(wǎng)頁授權(quán),用于獲取進(jìn)入頁面的用戶,靜默授權(quán),自動跳轉(zhuǎn)到回調(diào)頁面。用戶感知到的是直接進(jìn)入回調(diào)頁面(通常是業(yè)務(wù)頁面);
2、scope發(fā)起的網(wǎng)頁授權(quán),用于獲取用戶的基本信息。但該授權(quán)需要用戶手動同意,且由于用戶已同意,授權(quán)后無需關(guān)注即可獲取用戶的基本信息。
…
具體來說,網(wǎng)頁授權(quán)過程分為四個(gè)步驟:
1、引導(dǎo)用戶進(jìn)入授權(quán)頁面同意授權(quán)并獲取密碼;
2、網(wǎng)頁授權(quán)交換碼(不同于基礎(chǔ)支持);
3、如有需要,開發(fā)者可以刷新網(wǎng)頁授權(quán),避免過期;
4、通過網(wǎng)頁授權(quán)和獲取用戶基本信息(支持機(jī)制)。
附上微信授權(quán)微信公眾號開發(fā)的官方文檔。
以上是作者提取的一些比較關(guān)鍵的信息。當(dāng)然,還有更多的解釋。希望新手讀者先仔細(xì)閱讀官方文檔。
這里我補(bǔ)充一下,在上述流程的四個(gè)步驟中,除了第一步之外,其他三個(gè)步驟都需要在服務(wù)器端完成。前端的核心其實(shí)就是如何檢查判斷用戶的登錄狀態(tài),維護(hù)登錄狀態(tài)。
實(shí)現(xiàn)想法
眾所周知,Vue是前后端分離技術(shù)解決方案的產(chǎn)物。它是一個(gè)純前端應(yīng)用程序(服務(wù)器端渲染除外)。通常,當(dāng)用戶打開頁面并執(zhí)行頁面的js腳本時(shí),我們會異步請求服務(wù)端數(shù)據(jù),然后對相關(guān)邏輯進(jìn)行處理和判斷。實(shí)現(xiàn)微信授權(quán)登錄的前提是我們需要先判斷用戶是否需要登錄(或token)。當(dāng)用戶未登錄時(shí),需要經(jīng)過授權(quán)登錄過程。當(dāng)授權(quán)登錄成功時(shí),我們還需要在前端記錄登錄狀態(tài),這樣當(dāng)頁面切換時(shí),就不需要再次觸發(fā)授權(quán)登錄了。通過分析,我們可以看到,前端實(shí)際上可以做的是獲取微信服務(wù)器給我們的代碼Okdo Image to Jpeg J2k Jp2 Pcx Converter(圖片轉(zhuǎn)換工具),然后將代碼發(fā)送到我們的后端,這樣后端就可以完成后續(xù)的步驟來獲取用戶信息和生成用戶。那么我將整個(gè)過程整理如下:
(前端)檢查用戶是否登錄;(前端)若未登錄,引導(dǎo)用戶進(jìn)入授權(quán)頁面同意授權(quán),獲取代碼(前端)將獲取的代碼提交給后端(后端)兌換代碼為用戶憑證(后端)通過檢查用戶是否存在,是否需要注冊新用戶,獲取用戶id(后端)返回用戶信息;(前端)記錄用戶的登錄狀態(tài),并跳轉(zhuǎn)回預(yù)登錄頁面;
這個(gè)過程,我畫了一張圖Picture Information Extractor(圖片信息提取工具),如下:
上面的代碼
根據(jù)以上思路,現(xiàn)在開始編碼環(huán)節(jié)。筆者使用Vue3,Vue2開發(fā)者請根據(jù)情況進(jìn)行適當(dāng)調(diào)整。
為了方便用戶授權(quán)登錄邏輯,作者擬將授權(quán)登錄封裝為登錄頁面。這樣做的好處是,無論我們在哪里判斷需要登錄,都可以通過Vue的push方法直接跳轉(zhuǎn)到登錄頁面。
通常,并非我們應(yīng)用程序的所有頁面都需要登錄才能訪問。只有當(dāng)特定頁面被訪問時(shí),用戶才需要登錄。然后我們需要識別哪些頁面需要登錄認(rèn)證。這里我們可以使用Vue的meta屬性來識別picFormat(圖片格式轉(zhuǎn)換器),官方文檔對meta的解釋如下:
有時(shí),你可能想在路由上附加任意信息,例如轉(zhuǎn)換名稱、誰可以訪問該路由等。這些事情可以通過接收 對象的元屬性來實(shí)現(xiàn),并且可以在兩個(gè)路由上訪問地址和導(dǎo)航守衛(wèi)。
正好Vue官方有一個(gè)例子,如下:
const routes = [ { path: '/posts', component: PostsLayout, children: [ {path: 'new', component: PostsNew, // 需要登錄后才能訪問的頁面 meta: { requiresAuth: true } }, { path: ':id', component: PostsDetail, // 任何人都可訪問的頁面 meta: { requiresAuth: false } } ] } ]
接下來,我們可以在Vue的全局守衛(wèi)中獲取這個(gè)元信息來進(jìn)行登錄跳轉(zhuǎn)。
router.beforeEach((to, from) => { // 而不是去檢查每條路由記錄 // to.matched.some(record => record.meta.requiresAuth) if (to.meta.requiresAuth && !userStore.isLogin) { // 此路由需要授權(quán),請檢查是否已登錄 // 如果沒有,則重定向到登錄頁面 return { path: '/login', // 保存我們所在的位置,以便以后再來 query: { redirect: to.fullPath }, } } })
應(yīng)該補(bǔ)充的是,執(zhí)行 . 這和我們實(shí)際使用的登錄狀態(tài)維護(hù)方案有關(guān)。如果使用token方法,就是檢查token是否已經(jīng)存在。作者使用vuex保存token,然后使用插件將Store中的數(shù)據(jù)持久化到。
接下來,我們來看看具體的實(shí)現(xiàn):
login.vue:登錄組件
<script lang="ts"> import { defineComponent } from 'vue' import { jump2Auth, getUserInfo } from '@/hooks/useWechatAuth' import { userStore } from '@/store/modules/user' import { redirectTo, getRouteQuery } from '@/hooks/usePage' export default defineComponent({ name: 'Login', setup() { let code = getRouteQuery().code as string // 3.如果有code,則已經(jīng)授權(quán) if (code) { getUserInfo(code as string).then((res: any) => { // 記錄token userStore.saveToken(res.access_token) const redirect = userStore.userState.landPageRoute || '/' // 跳轉(zhuǎn)到授權(quán)前訪問的頁面 redirectTo(redirect) }) } else { // 1.記錄上一個(gè)頁面的地址 const { redirect } = getRouteQuery() if (redirect) { userStore.setLandPage(redirect as string) } // 2.跳轉(zhuǎn)授權(quán) const callbackUrl = window.location.origin + window.location.pathname jump2Auth(callbackUrl) } }, }) </script>
可以看出微信狼王授權(quán)碼注冊機(jī),登錄頁面實(shí)際上并沒有任何內(nèi)容。跳轉(zhuǎn)到這個(gè)頁面后,我們會直接跳轉(zhuǎn)到微信授權(quán)的頁面,授權(quán)回調(diào)也會返回到這個(gè)頁面。這時(shí)候我們會通過Get code參數(shù)的方式來獲取路由參數(shù)。
@/hooks/.ts:這個(gè)文件主要封裝了相關(guān)的常用方法。
import router from '@/router' import { cloneDeep } from 'lodash'import { toRaw } from 'vue' /** * 重定向 * @param path 路徑 */ export function redirectTo(path: string) { const { replace } = router replace({ path, }) } /** * 獲取路由上query參數(shù) */ export function getRouteQuery() { const { currentRoute } = router const { query } = currentRoute.value return cloneDeep(query) }
@/hooks/.ts:該文件封裝了微信授權(quán)與后端交互的請求。
import { useAxios } from '@/hooks/useAxios' /** * 獲取微信授權(quán)的跳轉(zhuǎn)地址 * @param callbackUrl 授權(quán)后回調(diào)鏈接 * @returns */ export function jump2Auth(callbackUrl: string) { useAxios({ url: '/api/wechat/auth', params: { redirect_url: callbackUrl, }, }).then((authUrl: any) => { if (process.env.NODE_ENV === 'development') { window.location.href = callbackUrl + '?code=test' } else { window.location.href = authUrl } }) } /** * 提交code進(jìn)行登錄 * @param code * @returns */ export async function getUserInfo(code: string) { const userInfo = await useAxios({ method: 'POST', url: '/api/wechat/auth', params: { code, }, }) return userInfo }
@/store//user.ts:全局狀態(tài)存儲,主要是登錄前記錄token和訪問頁面。
import { Module, VuexModule, Mutation, getModule, Action } from 'vuex-module-decorators' import store from '@/store' import { initialUnencryptedStorage } from '../globals' interface UserState { token: string landPageRoute: string } const NAME = 'user' // name: 模塊名字// namespaced 表示開啟命名空間 // dynamic設(shè)置為true時(shí),表示創(chuàng)建動態(tài)模塊,運(yùn)行時(shí)將模塊注冊到存儲中 // preserveState 如果數(shù)據(jù)有持久化,該變量為true時(shí)可以從storage中拿取初始值 @Module({ namespaced: true, name: NAME, dynamic: true, store, preserveState: Boolean(initialUnencryptedStorage[NAME]), }) export class User extends VuexModule { userState: UserState = { token: '', /** 登錄前訪問頁面 */ landPageRoute: '', } get isLogin(): boolean { return !!this.userState.token } @Mutation saveToken(token: string): void { this.userState.token = token } @Mutation setLandPage(route: string): void { this.userState.landPageRoute = route } } export const userStore = getModule(User)
作者使用 vuex- 將數(shù)據(jù)存儲在 store 中。這樣做的好處是用戶關(guān)閉頁面后可以再次訪問,即無需重新觸發(fā)微信授權(quán)流程,大大優(yōu)化了用戶體驗(yàn)。
總結(jié)
不得不說,Vue3 在代碼抽象和重用方面寫起來真的很舒服。希望大家也盡量按照官方的做法對邏輯代碼進(jìn)行解耦和分離,并一一生成hook,讓代碼看起來優(yōu)雅很多。經(jīng)過筆者嘗試演示,該方案無論從代碼的簡潔優(yōu)雅,還是業(yè)務(wù)需求的實(shí)現(xiàn)上,都已經(jīng)接近完美(請?jiān)试S我裝一波b)。當(dāng)然,可能有我在這里沒有發(fā)現(xiàn)的錯(cuò)誤或痛點(diǎn)。畢竟,從來沒有完美的架構(gòu)。在這里,歡迎您和我一起討論,提供更好的解決方案和想法。