很少写微信H5的项目,最近做了一个微信公众号的项目,想整理记录一下开发过程中遇到的一些问题,同时也顺便开启养成写文章的习惯,希望能让自己做出些改变,成长的更好。
废话不多说,进入正题。
微信公众号静默授权
在微信中访问第三方网页时,公众号可以通过微信网页授权机制来获取用户的基本信息,进而实现业务逻辑,无需用户进行额外的登录注册操作。
开发前准备
在开发前,需要有一个授权回调的域名,也就是h5网页的域名和接口的域名。
线上的话,这个需要在公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,设置授权回调域名,如www.qq.com
创建测试公众号
如果是测试环境,可以创建一个测试账号,大概过程如下:
- 进入微信公众帐号测试号申请系统,扫码登录
- 以下页面中,⚠️ 测试环境接口验证token 的URL和Token均由服务端同学提供;js接口安全域名,只输入前端页面域名,形式如www.qq.com,不用加协议http前缀
- 在“体验接口权限表” - “网页服务” - “网页帐号” - “网页授权获取用户基本信息” - 点击后面的“修改”按钮,出现如下弹窗,配置授权回调页面的域名
只输入域名,形式如www.qq.com
- 参考本地调试微信网页授权这篇,将hosts配置和nginx配置改成以上配置的测试域名。
网页授权的流程主要分为4步:
- 引导用户进入授权页面同意授权,获取code
- 通过code换取网页授权access_token(与基础支持中的access_token不同)
- 如果需要,开发者可以刷新网页授权access_token,避免过期
- 通过网页授权access_token和openid获取用户基本信息
⚠️ ️️关于网页授权access_token和普通access_token的区别:
微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。
一、先简单封装一个微信授权的方法,后面可以直接调用该方法走授权的逻辑
// @/utils/wxAuth.js
// appid - 公众号的唯一标识
// redirect_uri - 授权后重定向的回调URL地址, 需要编码
// state - 授权重定向后会带上state参数,可以填写a-zA-Z0-9的参数值,最多128字节,这里我默认设置的为1
// response_type - 返回类型,固定为code
// scope - 应用授权作用域,主要分两种:snsapi_base & snsapi_userinfo
function wechatAuth(redirect_url = window.location.href, state = 1) {
const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${encodeURIComponent(redirect_url)}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`;
window.location.href = authUrl;
}
export default wechatAuth;
⚠️ 这里讲一下scope两种情况的区别,可以根据需要选择:
- 以snsapi_base为scope发起的网页授权,不弹出授权页面,静默授权后直接自动跳转到回调页,该方式只能获取用户的openid。用户感知的就是直接进入了回调页(往往是业务页面)。
- 以snsapi_userinfo为scope发起的网页授权,弹出授权页面,需要用户手动同意授权,无论是否关注公众号,只要用户授权,即可获取该用户的基本信息,通过openid可以拿到用户的昵称、性别、所在地。
二、router.js,在beforeEach全局钩子函数中,判断是否需要用户授权
// @/utils/index.js
// 判断是否是微信浏览器
function isWeChat() {
const ua = window.navigator.userAgent.toLowerCase();
// eslint-disable-next-line eqeqeq
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
return true;
}
return false;
}
export default { isWeChat };
// router.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import index from '@/views/index/index.vue';
import { isWeChat } from '@/utils/';
import wechatAuth from '@/utils/wxAuth.js';
import { wechatLogin } from '@/api/';
import { Toast } from 'vant';
Vue.use(VueRouter);
// 页面路由
const routes = [
{
path: '/',
component: index,
meta: {
title: '首页',
},
},
...
];
const router = new VueRouter({
mode: 'history',
routes,
});
// 在全局钩子函数beforeEach中,判断是否进行微信授权
router.beforeEach(async (to, from, next) => {
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const isweChat = isWeChat();
// 如果不是在微信浏览器打开页面,提示用户微信打开
if (!isweChat) {
Toast('请在微信中打开');
return;
}
// 用户信息保存在了vuex中,如果没有userId,需要用户授权拿到code,然后传参给接口,获取用户userId
if (!store.getters.userId) {
// 获取目标路由上的code参数
const { code } = to.query;
// 如果URL上没有code,说明用户还没有授权,调用上面封装好的方法,进行微信授权
if (!code || code === 'undefined') {
// 进入授权之前,先保存一下未授权的页面地址,后面会用到
window.sessionStorage.setItem('curPageUrl', window.location.href);
// 调用上面封装的微信授权方法
wechatAuth();
return;
}
// 微信授权后,会直接跳转redirect_uri并带上code参数,如果回调URL上有code参数,说明用户授权过了,已经进入了授权后的回调页,这个时候,将URL上的code作为参数,传递给接口,去获取用户的userId
// ⚠️ ️wechatLogin 是我们项目中用到的一个接口,接口会根据code返回用户的userId
await wechatLogin({
code,
// 因为项目中暂时未用到分享功能,所以inviterId传空即可
inviterId: '',
}).then((res) => {
// 接口成功的回调中,保存接口返回的用户信息(userId和token)
store.commit('SET_USER_INFO', res.data);
}).catch(() => {
// 授权后的code只能使用一次,所以在授权过的带code的回调页上刷新页面,就会导致接口调用失败。当然,接口本身也可能因为其他原因而失败。
// 如果调用接口失败,需要再次进行授权,拿到一个新的code。如果是授权过的页面本身是有code的,重复走授权会在URL后面添加多个code,取code的时候就有问题了,这个时候,我们就用到前面没有code时,保存的那个url地址啦
const url = window.sessionStorage.getItem('curPageUrl');
wechatAuth(url);
});
}
// 拿到用户的userId后,就可以正常调用next()来resolve这个钩子函数了。
if (store.getters.userId) {
next();
}
});
关于微信授权的更多内容详见:微信官方文档-微信网页授权