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的,并且是靜默授權并自動跳轉到回調(diào)頁的。用戶感知的就是直接進入了回調(diào)頁(往往是業(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ù)情況做適當調(diào)整。
為了方便喚起用戶授權登錄邏輯,筆者打算將授權登錄封住為一個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)容,跳轉到該頁面后,我們會直接重定向到微信授權的頁面,授權回調(diào)回來也會回到該頁面,此時我們再通過獲取路由參數(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 授權后回調(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進行登錄 * @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-08VUE中Echarts的resize事件報錯和移除windows的事件問題
這篇文章主要介紹了VUE中Echarts的resize事件報錯和移除windows的事件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07