关于vue3:Vue3项目中微信授权登录的优雅实现

7次阅读

共计 5408 个字符,预计需要花费 14 分钟才能阅读完成。

前言

微信受权登录是做微信公众号开发始终绕不开的话题,而且整个受权登录流程的实现,是须要前后端配合一起实现的。在过来前后端还未分离的年代,兴许咱们前端并不需要太过关怀受权的具体实现。然而当初都 2021 年了,前后端拆散的架构大行其道,如何在前后端拆散的状况下实现微信受权登录就成了明天要探讨的重点问题。

筹备

首先,咱们还是须要先梳理下微信受权整个流程是怎么的,这里我就间接将官网文档搬来:

如果用户在微信客户端中拜访第三方网页,公众号能够通过微信网页受权机制,来获取用户根本信息,进而实现业务逻辑。

对于网页受权的两种 scope 的区别阐明
1、以 snsapi_base 为 scope 发动的网页受权,是用来获取进入页面的用户的 openid 的,并且是静默受权并主动跳转到回调页的。用户感知的就是间接进入了回调页(往往是业务页面)
2、以 snsapi_userinfo 为 scope 发动的网页受权,是用来获取用户的根本信息的。但这种受权须要用户手动批准,并且因为用户批准过,所以毋庸关注,就可在受权后获取该用户的根本信息。

具体而言,网页受权流程分为四步:
1、疏导用户进入受权页面批准受权,获取 code
2、通过 code 换取网页受权 access_token(与根底反对中的 access_token 不同)
3、如果须要,开发者能够刷新网页受权 access_token,防止过期
4、通过网页受权 access_token 和 openid 获取用户根本信息(反对 UnionID 机制)

这里附上微信公众号开发之微信受权的官网文档。

以上是笔者提炼进去的比拟要害的几点信息,当然还有更多的阐明,心愿老手读者们还是先认真看完官网文档。

这里我再补充阐明下,以上流程的 4 个步骤中,除了第一步以外,另外三步都是须要在服务器端去实现的。前端要做的外围其实是怎么进行用户登录状态的查看判断和登录状态的保护。

实现思路

大家都晓得,Vue 是前后端拆散技术计划下的产物,它是一个纯前端利用(服务端渲染除外)。通常咱们是须要在用户关上页面,执行到页面的 js 脚本时,咱们再异步去申请服务端数据,再进行相干逻辑的解决和判断。咱们要实现微信受权登录的前提是,须要先判断用户是否须要登录(cookie 或者 token)。当用户未登录时,才须要走受权登录流程,当受权登录胜利后,咱们也须要在前端记录好登录状态,以不便在页面切换时,不必再次触发受权登录。再通过剖析可知,前端其实能做的就是获取微信服务器给咱们的 code,再将 code 给咱们的后端,让后端实现后续步骤拿到用户信息后生成用户。那么整个过程我再梳理如下:

  1. (前端)检查用户是否登录;
  2. (前端)如果未登录,疏导用户进入受权页面批准受权,获取 code
  3. (前端)将获取到的 code 提交给后端
  4. (后端)通过 code 换取用户凭证 openid
  5. (后端)通过 openid 检查用户是否存在,是否须要注册新用户,并获取用户 id
  6. (后端)返回用户信息;
  7. (前端)记录用户登录状态,跳回登录前页面;

这个过程,我画了个图,如下:

上代码

依据以上思路,当初开始编码环节。笔者采纳的是 Vue3,Vue2 的开发者还请依据状况做适当调整。
为了不便唤起用户受权登录逻辑,笔者打算将受权登录封住为一个 login 页面,这样做的益处是咱们在任何判断到须要登录的中央间接通过 Vue Router 的 push 办法跳转到登录页面即可。
通常状况下,咱们的利用并不是所有页面都须要登录后能力拜访,只有在拜访特定页面时候,才须要用户登录,那么咱们就须要标识哪些页面须要进行登录鉴权。这里咱们能够利用 Vue Router 的 meta 属性来进行标识,官网文档对 meta 解释如下:

有时,你可能心愿将任意信息附加到路由上,如过渡名称、谁能够拜访路由等。这些事件能够通过接管属性对象的 meta 属性来实现,并且它能够在路由地址和导航守卫上都被拜访到。

刚好 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 的全局守卫 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 的实现。这里和咱们理论采纳的登录态保护计划无关,如果是采纳 token 形式的话,那就是查看 token 是否曾经存在。笔者采纳了 vuex 作为来保留 token,而后借助插件来将 Store 中的数据长久化到 localStorage。

接下来咱们来看具体的实现:
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,则曾经受权
    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 页面其实并没有什么内容,跳转到该页面后,咱们会间接重定向到微信受权的页面,受权回调回来也会回到该页面,此时咱们再通过获取路由参数的形式获取 code 参数。
@/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 参数
 */
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: 全局状态存储,次要是记录 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 时,示意创立动静模块,运行时将模块注册到存储中
// preserveState 如果数据有长久化,该变量为 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 中数据存储到了 localStorage,这样做的益处是用户敞开页面后,再次拜访,即不必从新触发微信受权流程,大大优化了用户体验。

总结

不得不说,Vue3 在代码形象和复用这块上,写起来着实难受很多,心愿大家也也尝试着依照官网实际那样,多将逻辑代码解耦抽离,生成一个个的 hook,这样代码就显得优雅多啦。该计划通过笔者尝试论证,不论是代码整洁优雅水平,还是业务需要的实现上,都简直完满(请容我装一波 b)。当然,这里可能存在我没发现的 bug 或者痛点,毕竟素来没有美中不足的架构嘛,这里也欢送看官大佬们和我交换探讨,提供更好的计划和想法。

写在最初

最近工作略微闲了些,笔者打算用 Vue3+ts+vant 从 0 开始实现一个公众号利用并将开发过程分享进去,也算是激励本人继续学习的一个形式吧。那么,笔者做的是怎么的一个公众号利用呢?是的,思考到看官老爷们写代码太忙,没空找对象,所以笔者打算做一个调配对象的公众号利用(是不是很刺激啊)。他人都思考你技术够不够好,只有我关怀你有没有对象哈哈哈。欢送扫下边二维码体验,性能还在不断完善中,如果有想法和倡议,欢送找我探讨哦。

同时,为不便大家更好的探讨微信公众号开发相干技术,笔者也建了一个微信群,欢送退出和大家一起学习成长。增加笔者微信 imwty2020,即可进群

正文完
 0