Vue3項目中優(yōu)雅實現(xiàn)微信授權登錄的方法
前言
微信授權登錄是做微信公眾號開發(fā)一直繞不開的話題,而且整個授權登錄流程的實現(xiàn),是需要前后端配合一起完成的。在過去前后端還未分離的年代,也許我們前端并不需要太過關心授權的具體實現(xiàn)。然而現(xiàn)在都2021年了,前后端分離的架構大行其道,如何在前后端分離的情況下實現(xiàn)微信授權登錄就成了今天要探討的重點問題。
準備
首先,我們還是需要先梳理下微信授權整個流程是怎樣的,這里我就直接將官方文檔搬來:
如果用戶在微信客戶端中訪問第三方網(wǎng)頁,公眾號可以通過微信網(wǎng)頁授權機制,來獲取用戶基本信息,進而實現(xiàn)業(yè)務邏輯。
...
關于網(wǎng)頁授權的兩種scope的區(qū)別說明
1、以snsapi_base為scope發(fā)起的網(wǎng)頁授權,是用來獲取進入頁面的用戶的openid的,并且是靜默授權并自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業(yè)務頁面)
2、以snsapi_userinfo為scope發(fā)起的網(wǎng)頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,并且由于用戶同意過,所以無須關注,就可在授權后獲取該用戶的基本信息。
...
具體而言,網(wǎng)頁授權流程分為四步:
1、引導用戶進入授權頁面同意授權,獲取code
2、通過code換取網(wǎng)頁授權access_token(與基礎支持中的access_token不同)
3、如果需要,開發(fā)者可以刷新網(wǎng)頁授權access_token,避免過期
4、通過網(wǎng)頁授權access_token和openid獲取用戶基本信息(支持UnionID機制)
以上是筆者提煉出來的比較關鍵的幾點信息,當然還有更多的說明,希望新手讀者們還是先認真看完官方文檔。
這里我再補充說明下,以上流程的4個步驟中,除了第一步以外,另外三步都是需要在服務器端去完成的。前端要做的核心其實是怎么進行用戶登錄狀態(tài)的檢查判斷和登錄狀態(tài)的維護。
實現(xiàn)思路
大家都知道,Vue是前后端分離技術方案下的產(chǎn)物,它是一個純前端應用(服務端渲染除外)。通常我們是需要在用戶打開頁面,執(zhí)行到頁面的js腳本時,我們再異步去請求服務端數(shù)據(jù),再進行相關邏輯的處理和判斷。我們要實現(xiàn)微信授權登錄的前提是,需要先判斷用戶是否需要登錄(cookie或者token)。當用戶未登錄時,才需要走授權登錄流程,當授權登錄成功后,我們也需要在前端記錄好登錄狀態(tài),以方便在頁面切換時,不用再次觸發(fā)授權登錄。再通過分析可知,前端其實能做的就是獲取微信服務器給我們的code,再將code給我們的后端,讓后端完成后續(xù)步驟拿到用戶信息后生成用戶。那么整個過程我再梳理如下:
- (前端)檢查用戶是否登錄;
- (前端)如果未登錄,引導用戶進入授權頁面同意授權,獲取code
- (前端)將獲取到的code提交給后端
- (后端)通過code換取用戶憑證openid
- (后端)通過openid檢查用戶是否存在,是否需要注冊新用戶,并獲取用戶id
- (后端)返回用戶信息;
- (前端)記錄用戶登錄狀態(tài),跳回登錄前頁面;
這個過程,我畫了個圖,如下:

上代碼
根據(jù)以上思路,現(xiàn)在開始編碼環(huán)節(jié)。筆者采用的是Vue3,Vue2的開發(fā)者還請根據(jù)情況做適當調整。
為了方便喚起用戶授權登錄邏輯,筆者打算將授權登錄封住為一個login頁面,這樣做的好處是我們在任何判斷到需要登錄的地方直接通過Vue Router的push方法跳轉到登錄頁面即可。
通常情況下,我們的應用并不是所有頁面都需要登錄后才能訪問,只有在訪問特定頁面時候,才需要用戶登錄,那么我們就需要標識哪些頁面需要進行登錄鑒權。這里我們可以利用Vue Router的meta屬性來進行標識,官方文檔對meta解釋如下:
有時,你可能希望將任意信息附加到路由上,如過渡名稱、誰可以訪問路由等。這些事情可以通過接收屬性對象的meta屬性來實現(xiàn),并且它可以在路由地址和導航守衛(wèi)上都被訪問到。
剛好Vue Router官方就有示例,如下:
const routes = [
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 需要登錄后才能訪問的頁面
meta: { requiresAuth: true }
},
{
path: ':id',
component: PostsDetail,
// 任何人都可訪問的頁面
meta: { requiresAuth: false }
}
]
}
]
接下來我們就可以在Vue Router的全局守衛(wèi)beforeEach中獲取到這個元信息從而做登錄跳轉了
router.beforeEach((to, from) => {
// 而不是去檢查每條路由記錄
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !userStore.isLogin) {
// 此路由需要授權,請檢查是否已登錄
// 如果沒有,則重定向到登錄頁面
return {
path: '/login',
// 保存我們所在的位置,以便以后再來
query: { redirect: to.fullPath },
}
}
})
需要補充說明的是,userStore.isLogin的實現(xiàn)。這里和我們實際采用的登錄態(tài)維護方案有關,如果是采用token方式的話,那就是檢查token是否已經(jīng)存在。筆者采用了vuex作為來保存token,然后借助插件來將Store中的數(shù)據(jù)持久化到localStorage。
接下來我們來看具體的實現(xiàn):
login.vue: 登錄組件
<template>
<div class="login"></div>
</template>
<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)授權
if (code) {
getUserInfo(code as string).then((res: any) => {
// 記錄token
userStore.saveToken(res.access_token)
const redirect = userStore.userState.landPageRoute || '/'
// 跳轉到授權前訪問的頁面
redirectTo(redirect)
})
} else {
// 1.記錄上一個頁面的地址
const { redirect } = getRouteQuery()
if (redirect) {
userStore.setLandPage(redirect as string)
}
// 2.跳轉授權
const callbackUrl = window.location.origin + window.location.pathname
jump2Auth(callbackUrl)
}
},
})
</script>
可以看到,login頁面其實并沒有什么內(nèi)容,跳轉到該頁面后,我們會直接重定向到微信授權的頁面,授權回調回來也會回到該頁面,此時我們再通過獲取路由參數(shù)的方式獲取code參數(shù)。
@/hooks/usePage.ts: 該文件主要是封裝了router相關的常用方法
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/useWechatAuth.ts:該文件封裝了微信授權與后端交互的請求
import { useAxios } from '@/hooks/useAxios'
/**
* 獲取微信授權的跳轉地址
* @param callbackUrl 授權后回調鏈接
* @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進行登錄
* @param code
* @returns
*/
export async function getUserInfo(code: string) {
const userInfo = await useAxios({
method: 'POST',
url: '/api/wechat/auth',
params: {
code,
},
})
return userInfo
}
@/store/modules/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設置為true時,表示創(chuàng)建動態(tài)模塊,運行時將模塊注冊到存儲中
// preserveState 如果數(shù)據(jù)有持久化,該變量為true時可以從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>(User)
筆者借助vuex-persistedstate插件將store中數(shù)據(jù)存儲到了localStorage,這樣做的好處是用戶關閉頁面后,再次訪問,即不用重新觸發(fā)微信授權流程,大大優(yōu)化了用戶體驗。
總結
不得不說,Vue3在代碼抽象和復用這塊上,寫起來著實舒服很多,希望大家也也嘗試著按照官方實踐那樣,多將邏輯代碼解耦抽離,生成一個個的hook,這樣代碼就顯得優(yōu)雅多啦。該方案經(jīng)過筆者嘗試論證,不論是代碼整潔優(yōu)雅程度,還是業(yè)務需求的實現(xiàn)上,都幾乎完美(請容我裝一波b)。當然,這里可能存在我沒發(fā)現(xiàn)的bug或者痛點,畢竟從來沒有十全十美的架構嘛,這里也歡迎看官大佬們和我交流探討,提供更好的方案和想法。
到此這篇關于Vue3項目中優(yōu)雅實現(xiàn)微信授權登錄的文章就介紹到這了,更多相關Vue3微信授權登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在vue項目中使用codemirror插件實現(xiàn)代碼編輯器功能
這篇文章主要介紹了在vue項目中使用codemirror插件實現(xiàn)代碼編輯器功能(代碼高亮顯示及自動提示),本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08
VUE中Echarts的resize事件報錯和移除windows的事件問題
這篇文章主要介紹了VUE中Echarts的resize事件報錯和移除windows的事件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07

