vue微信授权登录(亲测)
准备:
一、微信公众平台测试帐号申请
1、打开微信公众平台测试帐号申请地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
通过微信扫一扫授权就能进入到测试号管理页面。
二、设置授权域名
在测试号管理页面,需要修改两个地方
第一个:将地址换成外网访问地址:
第二个**(此时不需要前面的http://)**:
如果报redirect_uri参数错误,可检查这步是否去除前面的http://
除了修改上面两个地方以外,作为测试号,还需要将当前页面前面的测试号信息发给后台
现在,前台的基本配置已经完成了。
微信网页授权文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
由微信的官方文档得知,授权登陆共分为四步:
1 第一步:用户同意授权,获取code
2 第二步:通过code换取网页授权access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
其中,第一步由前端来做,第二步前端把code传到后端,后端获取access_token,第三步第四步则需要后端完成。
写代码之前需要明确下需求,整理下思路,先想再写是个好习惯。
我们需要做的是:
1、前端在需要获取用户信息的页面调起登录,某些页面不需要,比如首页、某些展示静态页。
2、微信返回,前端截取返回url中的code传给后台,并把后台返回的Token存到Cookie和axios的header头中。
3、如果Cookie中已经有Token了,调另一个接口,判断token是否已经过期,过期重新登录。
一、微信授权(history模式/hash模式)。
1、history模式下的微信授权登录
查阅Vue Router修改mode 为history 去掉“#/”符号,也需要后台配合改一下,不然页面会404。
mode: "history"
在路由守卫before-each.js的beforeEachHandler中写登录代码
白名单判断:
if ((to.meta && to.meta.needLogin)) {//授权操作
}else {//白名单内不做授权判断,直接next
next()
}
第一步:授权,1:
const appid = "wxd49a9705a0c6464a"; // 公众号的唯一标识
//授权后重定向的回调链接地址, 请使用 encodeURIComponent 对链接进行处理
const redirect_uri = encodeURIComponent(window.location.href.split("?")[0]);
const scope = "snsapi_userinfo" //弹出授权页面
const urls = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=STATE#wechat_redirect `
window.location.href = urls
参数说明:
第二步:授权之后截取url中的code传给后台,2:
var code = to.query.code //History模式直接从url获取code
//前端将code传给后台,并把后台返回的Token存到Cookie和axios的header头中
const result = await commonApi.getUserInfo2({ code: code })
if (result.code == 200) {
Cookie.set("Token", result.token)
axios.defaults.headers.common['Token'] = result.token
next()
}
第三步:如果Cookie中有Token,3:
// 获取本地token
const hasToken = Cookies.get("Token");
if (hasToken) {
console.log("有token");
//去 获取用户信息
next()
}else{//去执行第一步:授权
console.log("无token")
}
2、hash模式下的微信授权登录
微信授权登录验证会把网址中的#号去掉,这样在跳转的时候Vue拿不到Code。所以在第二步做了以下处理
//Vue - router hash模式微信登录授权验证,#号处理
let href = window.location.href;
if (href.includes("code")) { //url包括 com/?code 证明为从微信跳转回来的
var url = href.substring(0, href.length - 2); //vue自动在末尾加了 #/ 符号,截取去掉
var jingPosit = url.indexOf("?code"); //获取?code位置
var urlLeft = url.substring(0, jingPosit);//url左侧部分
var urlRight = url.substring(jingPosit, url.length); //url右侧部分
console.log(urlLeft + "#/" + urlRight);
window.location.href = urlLeft + "#/" + urlRight;//拼接跳转
}
获取code方式换成:
var code = getQueryString("code");//hash模式获取url中code
function getQueryString(name) {//解析url中的参数
var arr = (location.hash || "")
.substr(location.hash.indexOf("?") + 1)
.replace(/^\#/, "")
.split("&");
var params = {};
for (var i = 0; i < arr.length; i++) {
var data = arr[i].split("=");
if (data.length == 2 && data[0] == name) {
return data[1];
}
}
return null;
}
二、完整代码(hash模式为例,如果改为history模式,去掉相对应代码)
// 页面路由拦截
async function beforeEachHandler(to, from, next) {
if ((to.meta && to.meta.needLogin)) {
try {
// 获取本地token
const hasToken = Cookies.get("Token");
if (hasToken) {
console.log("有token");
//去 获取用户信息接口
next()
} else {
console.log("无token")
var code = getQueryString("code");//hash模式获取url中code
//var code = to.query.code //History 模式从url获取code
if (code) {//如果存在code,则拿code去后台获取openid拿到用户信息token
Cookies.set("Token", "dhjshruwy3888384")//没有对接口,token先写死
next()
//接口:前端将code传给后台,并把后台返回的Token存到Cookie和axios的header头中
// const result = await commonApi.getUserInfo2({ code: code })
// if (result.code == 200) {
// sessionStorage.setItem("Token", result.token)
// axios.defaults.headers.common['Token'] = result.token
// next()
// }
} else {//如果不存在code,url中包含code,说明已授权
//hash模式微信登录授权验证,#号处理(如果在history模式下,这段#号处理if,else可删除)
let href = window.location.href;
if (href.includes("code")) { //url包括 com/?code 证明为从微信跳转回来的
//vue自动在末尾加了 #/ 符号,截取去掉
var url = href.substring(0, href.length - 2);
var jingPosit = url.indexOf("?code"); //获取?code位置
var urlLeft = url.substring(0, jingPosit);//url左侧部分
var urlRight = url.substring(jingPosit, url.length); //url右侧部分
console.log(urlLeft + "#/" + urlRight);
window.location.href = urlLeft + "#/" + urlRight;//拼接跳转
//授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
const redirect_uri = encodeURIComponent(window.location.href.split("?")[0]);
//应用授权作用域:snsapi_userinfo(弹出授权页面,可通过openId拿到昵称、性别、所在地。)
const scope = "snsapi_userinfo"
// 应用授权作用域:snsapi_base (不弹出授权页面,直接跳转,只能获取用户openId)
}
}
}
} catch (error) {
console.error(error, "-------beforeEach错误日志-----")
next(false)
}
} else {
next()
}
}
function getQueryString(name) {//hash模式中需要用到解析url中的参数的函数
var arr = (location.hash || "")
.substr(location.hash.indexOf("?") + 1)
.replace(/^\#/, "")
.split("&");
var params = {};
for (var i = 0; i < arr.length; i++) {
var data = arr[i].split("=");
if (data.length == 2 && data[0] == name) {
return data[1];
}
}
return null;
}
总结:
1、我们只要能拿到code给后端,后端去拿openid,就算授权成功。
2、redirect_uri参数错误,检查下配置域名是否去除http://
3、response_type参数错误,检查redirect_uri 是否 encodeURIComponent
4、hash模式下:授权后重定向的回调链接地址反复授权,需要对#/处理,获取到code
5、如果改为history模式,需要后端配合修改nginx配置,否则部署会出现404
例子在:
手机端vue项目初始化工程模板:
http://47.107.67.231:8888/project-template/mobileInitProject feature/wechat-auth分支下面