什么时候需要授权
- 微信网页的二次开发,如果需要获取微信账号的信息如openId、头像、昵称等,实现登录、支付等功能需要进行微信网页授权。
- 进行授权操作需要确保微信公众号拥有授权作用域、公众号的appId、网页的项目要部署到域名服务器地址(IP地址无法完成授权操作)、授权地址要在公众号进行白名单配置、运行在微信浏览器上。
// 微信浏览器判断
export function isWeixin() {
let ua = navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) == 'micromessenger';
}
静默授权与非静默授权
- 静默授权是不需要用户点击确认的,只能获取到openId。
- 非静默授权是需要用户点击确认的,可以获取到更多的用户信息。
- 获取code的链接scope参数决定了进行的是哪种授权。静默scope=snsapi_base,非静默scope=snsapi_userinfo
获取code
这个code只能使用一次,获取后有效期是5分钟,获取成功后会自动拼在回调地址redirect_uri上。
/**
* 进行微信静默授权
config.SHARE_URL是config.js的文件的重定向地址,必须是域名地址
*/
export function getWxAuthBase() {
let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${config.APPID
}&redirect_uri=${encodeURIComponent(
config.SHARE_URL +
"?shareRedirect=" +
encodeURIComponent(location.href)
)}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
location.href = url;
}
/**
* 微信授权(非静默)
*/
export function getWxAuthUserInfo() {
let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${config.APPID
}&redirect_uri=${encodeURIComponent(
config.SHARE_URL +
"?shareRedirect=" +
encodeURIComponent(location.href)
)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`;
location.href = url;
}
获取access_token
access_token有效期为30天,过期了可以刷新(参考开放文档)。
/**
* 获取用户微信openId
* @param {string} params.code 用户授权的code
* @param {string} params.type 类型
*/
export function getAccessToken(params) {
return axios.post('ssg/wechat/get-access-token', {
grantType: 'authorization_code',
type: 'nldmx',
...params
});
}
常见错误40029: 使用拿到的code去获取access_token返回40029错误,网上很多说是code被使用了,但被使用了再次使用好像报的是另一个错误,提示是:code has been used.我遇到这个错误时第一次访问的地址是ip地址,可能是这个原因。
获取微信用户信息
/**
* 获取微信授权信息(openId和accessToken)
* @param {string|undefined} code 微信授权的code
*/
export function getWxAuthData(code) {
if (!code) {
var wxAuthDataStr = sessionStorage.getItem('wx_auth_data');
if (!wxAuthDataStr) {
console.log("获取微信授权信息失败!");
return Promise.resolve();
};
return Promise.resolve(JSON.parse(wxAuthDataStr));
}
return getAccessToken({ code }).then((res) => {
if (res.code === 0&&res.data.code===0) {
sessionStorage.setItem('wx_auth_data', JSON.stringify(res.data));
return res.data;
} else {
var wxAuthDataStr = sessionStorage.getItem('wx_auth_data');
if (!wxAuthDataStr) {
console.log("获取微信授权信息失败~");
return '';
};
return JSON.parse(wxAuthDataStr);
}
})
}
用户信息保存在本地,由于code只能使用一次,但是用户信息可能会在多个地方被使用,所以避免频繁地去获取code,我们可以把用户信息保存在sessionstorage。
重定向页面
如果只是一个页面需要授权可以直接把它写在回调地址redirect_uri里,重定向是为了方便以后给其他网页地址进行授权,也可以免去在公众号频繁配置授权地址,有重定向页面就可以通过前端修改重定向的地址来达到给不同页面授权的操作。
// 重定页面向代码redirect.html
// 这个文件放在域名服务器上,然后把地址放在config.js文件里供授权时使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>加载中...</title>
</head>
<body>
<script>
// 页面重定向处理
var url = location.href.split('?')
var params = url[1].split('&')
var data = {}
params.forEach(function (n, i) {
var p = n.split('=')
data[p[0]] = p[1]
})
if (!!data.shareRedirect) {
var shareRedirect = decodeURIComponent(data.shareRedirect);
var href = shareRedirect + (data.code ? ((shareRedirect.indexOf('?') > -1 ? '&' : '?') + 'code=' + data.code) : '')
console.log(href)
window.location.href = href;
}
</script>
</body>
</html>
在页面上使用示例
这是vue项目。 这个页面在微信上打开,注意也要是域名地址。 流程:打开页面->跳转到微信授权页面->授权成功后跳转到重定向页面(url拼接上code)->重定向页面跳转回原来的页面(url携带code)-> 原来的页面拿到code去获取微信信息-> 拿到微信信息进行相关的业务操作
// 微信绑定走的流程
import { getWxAuthBase, getWxAuthData } from '@/api';
created(){
const code = this.$route.query.code; // 授权后获得
if(!code){
// 如没有授权则去授权,这里用到静默授权
getWxAuthBase();
return;
}
// 获取微信授权信息openId
getWxAuthData(code).then(res =>{
if(res.code === 0){
this.processData(this.uid, res.openId, 'wx') // 这是业务代码,拿到openId后在这里进行相关操作
}else{
console.log(res.text)
}
});
}
- 页面返回时的bug: 表现:触发手机的返回事件,这时无法正常退出页面而是刷新页面。原因是首次打开页面的时候返回栈存入url1,跳转到鉴权页面返回栈存入url2,鉴权完成之后跳转到重定向页面url3,再重定向到原来的页面url1。此时返回栈[url1,url2,url3,url1],所以不能直接通过返回退出该页面。
- 解决方案: 监听物理返回键,触发返回事件时禁止页面返回或者直接关闭浏览器。(这个监听需要用户交互才能生效,如用户点击一下页面)
export default {
mounted() {
// 方法一、直接关闭浏览器器
window.addEventListener('popstate', this.closeWXWindow,false);
// 方法二、禁止页面返回
// this.backChange();
// window.addEventListener('popstate', this.backChange,false);
},
methods:{
closeWXWindow() {
if (WeixinJSBridge) {
WeixinJSBridge.invoke('closeWindow', {}, () => {
console.error('关闭微信网页失败');
});
}
},
// backChange() {
// // 因为微信是经过了授权页面重定向,导致返回栈里存在多余的url
// // 导致微信返回事件触发时跳转的地址不是我们想要,这里push一个本页面地址禁止微信浏览器返回事件
// var state = {
// title: "title",
// url: "#"
// };
// window.history.pushState(state, "title", "#");
// },
},
destroyed () {
window.removeEventListener('popstate', this.closeWXWindow, false) // false阻止默认事件
// window.removeEventListener('popstate', this.backChange,false);
},
};