最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3項(xiàng)目中優(yōu)雅實(shí)現(xiàn)微信授權(quán)登錄的方法

 更新時(shí)間:2021年09月09日 09:39:46   作者:imwty  
用戶在微信端中訪問第三方網(wǎng)頁,可以通過微信網(wǎng)頁授權(quán)機(jī)制獲取用戶的基本信息,進(jìn)而實(shí)現(xiàn)所需要的業(yè)務(wù)邏輯,這篇文章主要給大家介紹了關(guān)于Vue3項(xiàng)目中優(yōu)雅實(shí)現(xiàn)微信授權(quán)登錄的相關(guān)資料,需要的朋友可以參考下

前言

微信授權(quán)登錄是做微信公眾號(hào)開發(fā)一直繞不開的話題,而且整個(gè)授權(quán)登錄流程的實(shí)現(xiàn),是需要前后端配合一起完成的。在過去前后端還未分離的年代,也許我們前端并不需要太過關(guān)心授權(quán)的具體實(shí)現(xiàn)。然而現(xiàn)在都2021年了,前后端分離的架構(gòu)大行其道,如何在前后端分離的情況下實(shí)現(xiàn)微信授權(quán)登錄就成了今天要探討的重點(diǎn)問題。

準(zhǔn)備

首先,我們還是需要先梳理下微信授權(quán)整個(gè)流程是怎樣的,這里我就直接將官方文檔搬來:

如果用戶在微信客戶端中訪問第三方網(wǎng)頁,公眾號(hào)可以通過微信網(wǎng)頁授權(quán)機(jī)制,來獲取用戶基本信息,進(jìn)而實(shí)現(xiàn)業(yè)務(wù)邏輯。

...

關(guān)于網(wǎng)頁授權(quán)的兩種scope的區(qū)別說明

1、以snsapi_base為scope發(fā)起的網(wǎng)頁授權(quán),是用來獲取進(jìn)入頁面的用戶的openid的,并且是靜默授權(quán)并自動(dòng)跳轉(zhuǎn)到回調(diào)頁的。用戶感知的就是直接進(jìn)入了回調(diào)頁(往往是業(yè)務(wù)頁面)

2、以snsapi_userinfo為scope發(fā)起的網(wǎng)頁授權(quán),是用來獲取用戶的基本信息的。但這種授權(quán)需要用戶手動(dòng)同意,并且由于用戶同意過,所以無須關(guān)注,就可在授權(quán)后獲取該用戶的基本信息。

...

具體而言,網(wǎng)頁授權(quán)流程分為四步:

1、引導(dǎo)用戶進(jìn)入授權(quán)頁面同意授權(quán),獲取code

2、通過code換取網(wǎng)頁授權(quán)access_token(與基礎(chǔ)支持中的access_token不同)

3、如果需要,開發(fā)者可以刷新網(wǎng)頁授權(quán)access_token,避免過期

4、通過網(wǎng)頁授權(quán)access_token和openid獲取用戶基本信息(支持UnionID機(jī)制)

這里附上微信公眾號(hào)開發(fā)之微信授權(quán)的官方文檔。

以上是筆者提煉出來的比較關(guān)鍵的幾點(diǎn)信息,當(dāng)然還有更多的說明,希望新手讀者們還是先認(rèn)真看完官方文檔。

這里我再補(bǔ)充說明下,以上流程的4個(gè)步驟中,除了第一步以外,另外三步都是需要在服務(wù)器端去完成的。前端要做的核心其實(shí)是怎么進(jìn)行用戶登錄狀態(tài)的檢查判斷和登錄狀態(tài)的維護(hù)。

實(shí)現(xiàn)思路

大家都知道,Vue是前后端分離技術(shù)方案下的產(chǎn)物,它是一個(gè)純前端應(yīng)用(服務(wù)端渲染除外)。通常我們是需要在用戶打開頁面,執(zhí)行到頁面的js腳本時(shí),我們?cè)佼惒饺フ?qǐng)求服務(wù)端數(shù)據(jù),再進(jìn)行相關(guān)邏輯的處理和判斷。我們要實(shí)現(xiàn)微信授權(quán)登錄的前提是,需要先判斷用戶是否需要登錄(cookie或者token)。當(dāng)用戶未登錄時(shí),才需要走授權(quán)登錄流程,當(dāng)授權(quán)登錄成功后,我們也需要在前端記錄好登錄狀態(tài),以方便在頁面切換時(shí),不用再次觸發(fā)授權(quán)登錄。再通過分析可知,前端其實(shí)能做的就是獲取微信服務(wù)器給我們的code,再將code給我們的后端,讓后端完成后續(xù)步驟拿到用戶信息后生成用戶。那么整個(gè)過程我再梳理如下:

  1. (前端)檢查用戶是否登錄;
  2. (前端)如果未登錄,引導(dǎo)用戶進(jìn)入授權(quán)頁面同意授權(quán),獲取code
  3. (前端)將獲取到的code提交給后端
  4. (后端)通過code換取用戶憑證openid
  5. (后端)通過openid檢查用戶是否存在,是否需要注冊(cè)新用戶,并獲取用戶id
  6. (后端)返回用戶信息;
  7. (前端)記錄用戶登錄狀態(tài),跳回登錄前頁面;

這個(gè)過程,我畫了個(gè)圖,如下:

上代碼

根據(jù)以上思路,現(xiàn)在開始編碼環(huán)節(jié)。筆者采用的是Vue3,Vue2的開發(fā)者還請(qǐng)根據(jù)情況做適當(dāng)調(diào)整。
為了方便喚起用戶授權(quán)登錄邏輯,筆者打算將授權(quán)登錄封住為一個(gè)login頁面,這樣做的好處是我們?cè)谌魏闻袛嗟叫枰卿浀牡胤街苯油ㄟ^Vue Router的push方法跳轉(zhuǎn)到登錄頁面即可。

通常情況下,我們的應(yīng)用并不是所有頁面都需要登錄后才能訪問,只有在訪問特定頁面時(shí)候,才需要用戶登錄,那么我們就需要標(biāo)識(shí)哪些頁面需要進(jìn)行登錄鑒權(quán)。這里我們可以利用Vue Router的meta屬性來進(jìn)行標(biāo)識(shí),官方文檔對(duì)meta解釋如下:

有時(shí),你可能希望將任意信息附加到路由上,如過渡名稱、誰可以訪問路由等。這些事情可以通過接收屬性對(duì)象的meta屬性來實(shí)現(xiàn),并且它可以在路由地址和導(dǎo)航守衛(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中獲取到這個(gè)元信息從而做登錄跳轉(zhuǎn)了

router.beforeEach((to, from) => {
  // 而不是去檢查每條路由記錄
  // to.matched.some(record => record.meta.requiresAuth)
  if (to.meta.requiresAuth && !userStore.isLogin) {
    // 此路由需要授權(quán),請(qǐng)檢查是否已登錄
    // 如果沒有,則重定向到登錄頁面
    return {
      path: '/login',
      // 保存我們所在的位置,以便以后再來
      query: { redirect: to.fullPath },
    }
  }
})

需要補(bǔ)充說明的是,userStore.isLogin的實(shí)現(xiàn)。這里和我們實(shí)際采用的登錄態(tài)維護(hù)方案有關(guān),如果是采用token方式的話,那就是檢查token是否已經(jīng)存在。筆者采用了vuex作為來保存token,然后借助插件來將Store中的數(shù)據(jù)持久化到localStorage。

接下來我們來看具體的實(shí)現(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)授權(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>

可以看到,login頁面其實(shí)并沒有什么內(nèi)容,跳轉(zhuǎn)到該頁面后,我們會(huì)直接重定向到微信授權(quán)的頁面,授權(quán)回調(diào)回來也會(huì)回到該頁面,此時(shí)我們?cè)偻ㄟ^獲取路由參數(shù)的方式獲取code參數(shù)。

@/hooks/usePage.ts: 該文件主要是封裝了router相關(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/useWechatAuth.ts:該文件封裝了微信授權(quán)與后端交互的請(qǐng)求

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/modules/user.ts: 全局狀態(tài)存儲(chǔ),主要是記錄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)建動(dòng)態(tài)模塊,運(yùn)行時(shí)將模塊注冊(cè)到存儲(chǔ)中
// 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>(User)

筆者借助vuex-persistedstate插件將store中數(shù)據(jù)存儲(chǔ)到了localStorage,這樣做的好處是用戶關(guān)閉頁面后,再次訪問,即不用重新觸發(fā)微信授權(quán)流程,大大優(yōu)化了用戶體驗(yàn)。

總結(jié)

不得不說,Vue3在代碼抽象和復(fù)用這塊上,寫起來著實(shí)舒服很多,希望大家也也嘗試著按照官方實(shí)踐那樣,多將邏輯代碼解耦抽離,生成一個(gè)個(gè)的hook,這樣代碼就顯得優(yōu)雅多啦。該方案經(jīng)過筆者嘗試論證,不論是代碼整潔優(yōu)雅程度,還是業(yè)務(wù)需求的實(shí)現(xiàn)上,都幾乎完美(請(qǐng)容我裝一波b)。當(dāng)然,這里可能存在我沒發(fā)現(xiàn)的bug或者痛點(diǎn),畢竟從來沒有十全十美的架構(gòu)嘛,這里也歡迎看官大佬們和我交流探討,提供更好的方案和想法。

到此這篇關(guān)于Vue3項(xiàng)目中優(yōu)雅實(shí)現(xiàn)微信授權(quán)登錄的文章就介紹到這了,更多相關(guān)Vue3微信授權(quán)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

雅江县| 依兰县| 松原市| 新闻| 新蔡县| 靖州| 双柏县| 万州区| 泰顺县| 噶尔县| 双江| 铜川市| 庆阳市| 建湖县| 通河县| 犍为县| 什邡市| 嘉祥县| 曲靖市| 阜南县| 正宁县| 汨罗市| 岳西县| 延吉市| 德兴市| 张家口市| 凤翔县| 怀化市| 乐陵市| 石楼县| 兰州市| 贡嘎县| 阳东县| 通州区| 都兰县| 西乡县| 涪陵区| 榆社县| 普兰县| 繁峙县| 台北县|