前言

微信受权登录是做微信公众号开发始终绕不开的话题,而且整个受权登录流程的实现,是须要前后端配合一起实现的。在过来前后端还未分离的年代,兴许咱们前端并不需要太过关怀受权的具体实现。然而当初都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,即可进群