以前開發(fā)微信公眾號(hào)授權(quán)登錄都是由后端來(lái)完成,但是最近在開發(fā)單頁(yè)面應(yīng)用時(shí),頁(yè)面路由不走后臺(tái),在網(wǎng)上查找了一些資料,了解了授權(quán)登錄的原理,便自己封裝的一個(gè)前端授權(quán)登錄的模塊。授權(quán)登錄比較順利完成了,但是遇見了授權(quán)完成后返回頁(yè)面的問(wèn)題,由于授權(quán)成功后微信會(huì)跳轉(zhuǎn)到用戶設(shè)置的redirect_uri鏈接,在按返回鍵返回的時(shí)候又會(huì)返回到微信授權(quán)的鏈接,造成了死循環(huán)。所以經(jīng)過(guò)層層怕坑,決心寫下此文章,做個(gè)記錄。
第一步:用戶同意授權(quán),獲取code
我是在收到后臺(tái)返回code值為需要授權(quán)登錄的時(shí)候跳轉(zhuǎn)到登錄頁(yè)面,在登錄頁(yè)面先判斷連接中是否有code,如果沒(méi)有跳轉(zhuǎn)授權(quán)鏈接。
在確保微信公眾賬號(hào)擁有授權(quán)作用域(scope參數(shù))的權(quán)限的前提下(服務(wù)號(hào)獲得高級(jí)接口后,默認(rèn)擁有scope參數(shù)中的snsapi_base和snsapi_userinfo),引導(dǎo)關(guān)注者打開如下頁(yè)面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“該鏈接無(wú)法訪問(wèn)”,請(qǐng)檢查參數(shù)是否填寫錯(cuò)誤,是否擁有scope參數(shù)對(duì)應(yīng)的授權(quán)作用域權(quán)限。
以上鏈接中的appid時(shí)申請(qǐng)公眾號(hào)獲得的appid,redirect_uri是用戶授權(quán)登錄成功后需要跳轉(zhuǎn)的鏈接,需要注意的是redirect_uri需要使用 urlEncode 進(jìn)行處理,其他參數(shù)請(qǐng)查看微信官方文檔https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
第二步:獲取授權(quán)登錄成功跳轉(zhuǎn)鏈接中的code值
可以使用一下函數(shù)獲取鏈接中的所有參數(shù)
function getUrlParams(){
var url = location.search; //獲取url攜帶的參數(shù)
var urlParams = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
var strs = str.split("&");
for(var i = 0; i < strs.length; i ++) {
urlParams[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return urlParams;
}
getUrlParams().code便是我們需要得到的內(nèi)容
第三步:將code通過(guò)ajax發(fā)送給后臺(tái)獲取后臺(tái)傳遞過(guò)來(lái)的token和其他用戶信息
有了code我們就可以發(fā)送給后臺(tái),后臺(tái)通過(guò)code解析出用戶信息并生成用戶和用戶的token返回給前端,這個(gè)時(shí)候前端需要將token和用戶信息保存下來(lái),我是使用本地存儲(chǔ)保存的token,用vuex來(lái)保存用戶信息。
第四步返回之前的頁(yè)面
因?yàn)樘D(zhuǎn)授權(quán)鏈接之前我是先跳轉(zhuǎn)下一個(gè)專門的登錄頁(yè)面,所按道理說(shuō)應(yīng)該向前返回3個(gè)頁(yè)面,分別為:第一次跳轉(zhuǎn)的登錄頁(yè)面,微信授權(quán)的頁(yè)面,微信授權(quán)成功后跳轉(zhuǎn)的鏈接(這里我配置的是登陸頁(yè)面),故使用 history.go(-3); 返回。這在開發(fā)者工具中每什么問(wèn)題,可以返回之前的頁(yè)面,但是在手機(jī)上卻不行,經(jīng)過(guò)嘗試發(fā)現(xiàn)手機(jī)只需要向前返回兩個(gè)頁(yè)面,但是考慮為了開發(fā)方便或者可能其他的手機(jī)或其他版本的微信需要向前返回3個(gè)頁(yè)面,所又想了一方法不管怎么樣都能返回到之前的頁(yè)面的方法,方法就是在登錄頁(yè)面(第一次跳轉(zhuǎn)的頁(yè)面中判斷是否又存儲(chǔ)的token,有的話就再返回一次),哈哈,機(jī)智,騷操作~~~
總結(jié):
為了方便自己封裝了一個(gè)模塊:
const wxApi = {
appid:"*****",
urlencode: function(url){
url = (url + '').toString();
return encodeURIComponent(url).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').
replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
},
author: function(redirect_uri){
redirect_uri = this.urlencode(redirect_uri)
var url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + this.appid + "&redirect_uri=" + redirect_uri + "&response_type=code" + "&scope=snsapi_userinfo" + "&state=STATE#wechat_redirect";
window.location.href = url;
},
getUrlParams: function(){
var url = location.search; //獲取url攜帶的參數(shù)
var urlParams = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
var strs = str.split("&");
for(var i = 0; i < strs.length; i ++) {
urlParams[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return urlParams;
}
}
module.exports = {
wxApi
}
使用方法:
- 引用模塊
import {wxApi} from "../common/wxApi.js"
- 判斷本地是否有taoke,有的話返回上一頁(yè)
if(localStorage.getItem('token')){
history.go(-1);
return
}
- 判斷當(dāng)前頁(yè)的鏈接是否有code值
if(wxApi.getUrlParams().code!=undefined){ //如果獲取到了微信code,則執(zhí)行獲取token的接口,拿到token需要往前返回兩個(gè)頁(yè)面
this.getToken(wxApi.getUrlParams().code)
return
}
// 如果沒(méi)有code則跳轉(zhuǎn)到微信的授權(quán)頁(yè)面
const redirect_uri = config.apiDomain + "/login";
wxApi.author(redirect_uri)
總的代碼就是:
import {wxApi} from "../common/wxApi.js"
export default {
data() {
return {}
},
created(){
if(localStorage.getItem('token')){
history.go(-1);
return
}
if(wxApi.getUrlParams().code!=undefined){
this.getToken(wxApi.getUrlParams().code)
return
}
const redirect_uri = config.apiDomain + "/login";
wxApi.author(redirect_uri)
},
methods: {
getToken(code){
this.$ajax.get("", {code}, res => {
if(res.data.code == 1) {
localStorage.setItem("token", res.data.token);
history.go(-2);
}
})
}
}
}