一游添、基本說明
微信小程序中對于用戶的登錄授權(quán)機制况褪,是非常重要的。許多的業(yè)務(wù)都必須拿到具體的微信數(shù)據(jù)才能進行渊季。我們在之前的微信小程序開發(fā)中朋蔫,因為業(yè)務(wù)比較簡單只有首頁,并且在進入小程序的時候就必須進行授權(quán)却汉,所以在操作的時候比較簡單驯妄。隨著業(yè)務(wù)的不斷增加,業(yè)務(wù)場景的增多合砂,之前的授權(quán)機制無法覆蓋所有的業(yè)務(wù)場景青扔。本次是在之前的授權(quán)機制的場景下,進行優(yōu)化和改版翩伪。前端和后臺進行配合微猖,完善微信的授權(quán)機制,達到適應(yīng)現(xiàn)有的業(yè)務(wù)缘屹。本文將講解從老版本到現(xiàn)版本的微信授權(quán)機制的改版優(yōu)化過程凛剥。
注:5月10日的授權(quán)方案可在第四部分直接查看
二、代碼說明
(1) 1.0版本微信授權(quán)
本文對具體的小程序端和后臺之間的微信授權(quán)不做具體的說明轻姿,詳情請看文檔犁珠。對于小程序我們封裝了兩個主要的工具模塊類。用戶模塊user.js,請求模塊request.js互亮。
user.js主要處理用戶的基本信息模塊犁享,包括授權(quán)管理和登錄,用戶其他數(shù)據(jù)的綁定等豹休。
var user = {
config:{},
userinfo:null,
// 初始化user模塊炊昆,校驗ukey
init(data){
this.config = data;
var self = this;
wx.checkSession({
success:function(res){
//存在登錄狀態(tài)
self.checkUkey();
},
fail:function(){
//過期-重新登錄
self.login();
}
});
},
// 發(fā)起微信登錄接口 獲取登錄憑證code
login(){
var self = this;
wx.login({
success:function(res){
self.config.code = res.code;
wx.setStorageSync(self.config.storage.code,res.code);
self.getWXUserInfo(true);
},
fail:function(res){
console.log("user login fail")
}
})
},
// 獲取用戶信息
getWXUserInfo(isthrough){
var self = this;
wx.getUserInfo({
success:function(res){
self.userinfo = res.userInfo;
wx.setStorageSync(self.config.storage.wxUser, res.userInfo);
if (isthrough) {
self.getUserUkey(res.rawData);
}
},
fail:function(res){
wx.openSetting({
success:function(res){
if(res.authSetting["scope.userInfo"]){
wx.getUserInfo({
success: res => {
// 可以將 res 發(fā)送給后臺解碼出 unionId
self.userinfo = res.userInfo;
wx.setStorageSync(self.config.storage.wxUser, res.userInfo);
if (isthrough) {
self.getUserUkey(res.rawData);
}
}
});
}
}
})
}
})
},
// 獲取Ukey
getUserUkey(rawData){
var self = this;
var data = {
code: wx.getStorageSync(self.config.storage.code),
rawData: rawData
};
wx.request({
url:self.config.requrl+"/Wdservice/api/wxLogin",
data: data,
method:"GET",
success:function(res){
res = res.data;
console.log(res);
if(res.code==1){
// 緩存Ukey
wx.setStorageSync(self.config.storage.ukey,res.result.ukey);
// 緩存Ukey存儲時間
wx.setStorageSync(self.config.storage.ukeytime,new Date().getTime());
}
}
})
},
// 檢驗ukey是否過期
checkUkey(){
var ukey = wx.getStorageSync(this.config.storage.ukey);
var ukeyTime = wx.getStorageSync(this.config.storage.ukeytime);
var tmpTime = new Date().getTime();
var token = wx.getStorageSync(this.config.storage.ukey_licaiyi);
if (tmpTime - ukeyTime >= 1000 * 60 * 60 * 12 || !token){
ukey = "";
this.login();
} else {
this.getWXUserInfo(false);
}
}
}
module.exports = user;
request.js主要對請求的再封裝,統(tǒng)一加入ukey∫っ校可對接口統(tǒng)一管理屏积,增加擴展性。
var app = getApp();
// 打包ukey和參數(shù)
function ukeyData (data) {
var copyData = JSON.parse(JSON.stringify(data));
var ukey = { "ukey": wx.getStorageSync(app.globalData.config.storage.ukey) };
var okUkeyData = Object.assign(copyData, ukey);
return okUkeyData;
}
function post(data){
req({
url:data.url,
method:"POST",
data:ukeyData(data.data),
succ:data.succ,
fail:data.fail,
complete:data.complete
})
}
function get(data){
req({
url:data.url,
method:"GET",
data: ukeyData(data.data),
succ:data.succ,
fail:data.fail,
complete:data.complete
})
}
function req(data){
wx.request({
url: data.url,
data: ukeyData(data.data),
method: data.method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
header: {
'content-type':'application/x-www-form-urlencoded',
}, // 設(shè)置請求的 header
success: function(res){
// success
res = res.data;
if(typeof data.succ === "function"){
data.succ(res);
}
},
fail:function(res){
if(typeof data.fail === "function"){
data.fail(res);
}
},
complete:function(res){
if(typeof data.complete === "function"){
data.complete(res);
}
}
})
}
const request = {
post:post,
get:get
}
module.exports = request;
具體使用:在app.js中引用user.js初始化該組件即可磅甩。每次程序啟動的時候會自動校驗ukey的有效性,如果沒有登錄或者ukey過期會重新登錄姥卢。
var user = require("/tools/user");
onLaunch: function () {
// 檢測用戶
user.init(config);
}
(2) 2.0版本微信授權(quán)
隨著業(yè)務(wù)的增加卷要,小程序添加了掃碼支付的功能。該功能在1.0版本的情況下會出現(xiàn)很重大的bug独榴。雖然在app.js中初始化了user.js組件并得到ukey等信息僧叉。但是跳轉(zhuǎn)到掃碼頁面的時候,因為登錄請求都是異步執(zhí)行的棺榔,在app.js完成授權(quán)之前瓶堕,界面上的接口已經(jīng)調(diào)用了。由于當時可能還沒有獲取到ukey導(dǎo)致界面上的接口報錯症歇,而獲取不到需要的數(shù)據(jù)郎笆,界面一片空白,又沒有刷新機制忘晤,導(dǎo)致出現(xiàn)嚴重的授權(quán)bug宛蚓。對于這個業(yè)務(wù)場景下1.0的微信授權(quán)已經(jīng)無法解決我們的問題,所以我們對user.js進行優(yōu)化设塔,以適應(yīng)該業(yè)務(wù)凄吏。
在user.js中添加登錄完成的回調(diào)succ
var user = {
config:{},
userinfo:null,
init(data){
this.config = data;
var self = this;
wx.checkSession({
success:function(res){
//存在登錄狀態(tài)
self.checkUkey();
},
fail:function(){
//過期-重新登錄
self.login(null);
}
});
},
login(succ){
var self = this;
wx.login({
success:function(res){
self.config.code = res.code;
wx.setStorageSync(self.config.storage.code,res.code);
self.getWXUserInfo(true, succ);
},
fail:function(res){
console.log("user login fail")
}
})
},
getWXUserInfo(isthrough, succ){
var self = this;
wx.getUserInfo({
success:function(res){
self.userinfo = res.userInfo;
wx.setStorageSync(self.config.storage.wxUser, res.userInfo);
if (isthrough) {
self.getUserUkey(res.rawData, succ);
}
},
fail:function(res){
wx.openSetting({
success:function(res){
if(res.authSetting["scope.userInfo"]){
wx.getUserInfo({
success: res => {
// 可以將 res 發(fā)送給后臺解碼出 unionId
self.userinfo = res.userInfo;
wx.setStorageSync(self.config.storage.wxUser, res.userInfo);
if (isthrough) {
self.getUserUkey(res.rawData, succ);
}
}
});
}
}
})
}
})
},
getUserUkey(rawData, succ){
var self = this;
var data = {
code: wx.getStorageSync(self.config.storage.code),
rawData: rawData
};
wx.request({
url:self.config.requrl+"/Invest/api/wxLogin",
data: data,
method:"GET",
success:function(res){
res = res.data;
if(res.code==1){
wx.setStorageSync(self.config.storage.ukey,res.result.ukey);
wx.setStorageSync(self.config.storage.ukeytime,new Date().getTime());
self.getAuthenticationUserUkey(rawData, res.result.oid, succ);
}
}
})
},
getAuthenticationUserUkey(rawData, openId, succ){
var self = this;
var data = {
code: wx.getStorageSync(self.config.storage.code),
rawData: JSON.parse(rawData),
openId: openId,
source: 2,
};
wx.request({
url: self.config.couresurl + "/WechatApi/authentication",
data: data,
method: "POST",
success: function (res) {
res = res.data;
if (res.resCode == 1) {
wx.setStorageSync(self.config.storage.ukey_licaiyi, res.resObject);
if (succ) {
succ();
}
}
}
})
},
checkUkey(){
var ukey = wx.getStorageSync(this.config.storage.ukey);
var ukeyTime = wx.getStorageSync(this.config.storage.ukeytime);
var tmpTime = new Date().getTime();
var token = wx.getStorageSync(this.config.storage.ukey_licaiyi);
if (tmpTime - ukeyTime >= 1000 * 60 * 60 * 12 || !token){
ukey = "";
this.login(null);
} else {
this.getWXUserInfo(false, null);
}
}
}
module.exports = user;
在掃碼的界面上我們首先校驗ukey,如果存在直接調(diào)用詳細的接口闰蛔,如果獲取不到ukey痕钢,說明微信尚未授權(quán),我們主動調(diào)用user模塊的login方法序六,在登錄的成功回調(diào)成功之后再執(zhí)行接口任连,便可解決該問題。
if (wx.getStorageSync(app.globalData.config.storage.ukey)) {
this.starToConfig();
} else {
app.globalData.wxUser.login(function(){
self.starToConfig();
});
}
該解決方案具有很大的局限性难咕,如果添加一個新的業(yè)務(wù)場景我們就需要多次判斷课梳,擴展性很差。但是對于只有這樣一個特殊場景來說余佃,還是適用的暮刃。
(3) 3.0版本微信授權(quán)
隨著業(yè)務(wù)的增加,微信小程序進行了更大的改版爆土,首頁不再強制需要授權(quán)椭懊。我的頁面則需要授權(quán)。在這樣的背景下,我們對授權(quán)機制和請求機制進行聯(lián)合氧猬,把授權(quán)的時機判斷放在后臺進行聯(lián)合判斷背犯。這樣就可以解決2.0遺漏下來的授權(quán)比較局限的問題。對于所有需要授權(quán)的地方后臺如果檢測到我們的接口用戶沒有授權(quán)盅抚,便返回統(tǒng)一的未授權(quán)的錯誤碼漠魏,小程序端對該狀態(tài)進行統(tǒng)一的操作,強制用戶登錄并在回調(diào)后執(zhí)行后續(xù)的接口妄均。
在request.js中柱锹, 我們對返回狀態(tài)進行了細分,分為請求成功丰包,請求錯誤禁熏,微信未授權(quán)等。
var app = getApp();
// 添加返回標識
var ResultCode = {
ResultCodeSucc: 1,
ResultCodeFail: -1,
ResultCodeNoAccredit: -2
};
function ukeyData (data) {
var okUkeyData = null;
if (data) {
var copyData = JSON.parse(JSON.stringify(data));
var ukey = { "ukey": wx.getStorageSync(app.globalData.config.storage.ukey) };
okUkeyData = Object.assign(copyData, ukey);
} else {
var ukey = { "ukey": wx.getStorageSync(app.globalData.config.storage.ukey) };
okUkeyData = ukey;
}
return okUkeyData;
}
function post(data){
req({
url:data.url,
method:"POST",
data:ukeyData(data.data),
succ:data.succ,
error:data.error,
no_accredit:data.no_accredit,
fail:data.fail,
complete:data.complete
})
}
function get(data){
req({
url:data.url,
method:"GET",
data: ukeyData(data.data),
succ:data.succ,
error: data.error,
no_accredit: data.no_accredit,
fail:data.fail,
complete:data.complete
})
}
function req(data){
wx.request({
url: data.url,
data: ukeyData(data.data),
method: data.method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
header: {
'content-type':'application/x-www-form-urlencoded',
}, // 設(shè)置請求的 header
success: function(res){
// success
res = res.data;
// 根據(jù)返回碼 分發(fā)行為
if (res.code == ResultCode.ResultCodeSucc) {
// 請求成功
if (typeof data.succ === "function") {
data.succ(res);
}
} else if (res.code == ResultCode.ResultCodeNoAccredit) {
if (typeof data.no_accredit === "function") {
// 微信未授權(quán)
app.globalData.wxUser.login(function () {
data.no_accredit(res);
});
}
} else {
// 請求錯誤
if (typeof data.error === "function") {
data.error(res);
}
}
},
fail:function(res){
// 請求失敗
if(typeof data.fail === "function"){
data.fail(res);
}
},
complete:function(res){
if(typeof data.complete === "function"){
data.complete(res);
}
}
})
}
const request = {
post:post,
get:get
}
module.exports = request;
接口改造:
// 預(yù)約活動
person_appointment_tap: function (e) {
if (!this.data.isAppointment) {
var self = this;
request.post({
url: app.globalData.config.requrl + "/Invest/Wx/subscribeMsg",
data: {
formid: e.detail.formId,
activity_id: self.data.activity_id
},
header: {
'content-type': 'application/x-www-form-urlencoded'
},
succ: function (res) {
// 成功
wx.showModal({
title: '預(yù)約成功',
content: "我們會在活動開始前給您發(fā)送消息提醒邑彪,請您及時關(guān)注",
showCancel: false,
confirmColor: "#ffcf00",
success: function (res) {
}
})
self.setData({ isAppointment: true });
},
error: function (res) {
// 失敗
prompt.showModal(res.code == 2 ? "您已經(jīng)預(yù)約過了瞧毙!" : "預(yù)約失敗,請稍后再試寄症!", null);
if (res.code == 2) {
self.setData({ isAppointment: true });
}
},
no_accredit: function (res) {
// 在授權(quán)成功后再次調(diào)用該接口即可
self.person_appointment_tap(e);
}
})
}
}
我們在app.js中刪除用戶初始化代碼宙彪,在需要用戶第一次授權(quán)的地方就進行初始化即可。在3.0中瘸爽,我們將初始化授權(quán)代碼放在我的單頁的onLoad方法中您访,之后只要在后臺需要授權(quán)權(quán)限的地方,檢測到未授權(quán)剪决,就可提示到前端灵汪,讓前端進行用戶登錄授權(quán)了。
(4) 4.0版本微信授權(quán)(5月10日微信小程序授權(quán)調(diào)整)
5月10日微信小程序官方突然發(fā)布了新的微信小程序授權(quán)機制柑潦,用戶首次授權(quán)必須使用按鈕的方式進行顯示授權(quán)享言。對于這次更新我們也在原有的基礎(chǔ)上進行了改版,因為小程序的入口繁多而且微信沒有繼承的概念和像window一樣的頂級浮層渗鬼,就導(dǎo)致我們必須對授權(quán)機制進行統(tǒng)一管理览露。我們這次刪除了user.js,將授權(quán)及整個登陸機制放在封裝好的授權(quán)組件內(nèi)進行統(tǒng)一管理譬胎。并在接口處處理登陸失效的情況差牛,這樣就能很好的解決這次微信新授權(quán)機制對我們的影響。
微信小程序獲取用戶信息接口優(yōu)化調(diào)整文章 : 地址
(1)comp-auto組件
// index.js
var app = getApp();
Component({
properties: {
},
data: {
show:false,
},
methods: {
open(){
this.setData({show:true});
},
close(){
this.setData({show:false});
},
// 微信授權(quán)btn授權(quán)完成后會將用戶信息返回堰乔,我們在這個方法中可以直接獲取到用戶數(shù)據(jù)
userauth(){
var self = this;
wx.getUserInfo({
success:function(res){
wx.setStorageSync(app.globalData.config.storage.wxUser, res.userInfo);
self.getUserUkey(res.rawData,true);
},
fail:function(res){
console.log("userinfo fail",res);
}
})
},
checkUkey() {
var ukey = wx.getStorageSync(app.globalData.config.storage.ukey);
var ukeyTime = wx.getStorageSync(app.globalData.config.storage.ukeytime);
var tmpTime = new Date().getTime();
if (tmpTime - ukeyTime >= 1000 * 60 * 60 * 12) {
wx.setStorageSync(app.globalData.config.storage.ukey, "");
this.login();
} else {
this.getWXUserInfo();
}
},
login(){
var self = this;
wx.login({
success:function(res){
wx.setStorageSync(app.globalData.config.storage.code,res.code);
self.getWXUserInfo();
},
fail:function(res){
console.log("user login fail")
}
})
},
getWXUserInfo() {
var self = this;
wx.getUserInfo({
success: function (res) {
wx.setStorageSync(app.globalData.config.storage.wxUser, res.userInfo);
self.getUserUkey(res.rawData, false);
},
fail: function (res) {
// 防止已授權(quán)的用戶偏化,主動在設(shè)置中關(guān)閉授權(quán)后,可以彈出授權(quán)組件
self.open();
}
})
},
getUserUkey(rawData, close) {
var data = {
code: wx.getStorageSync(app.globalData.config.storage.code),
rawData: rawData
}
var self = this;
wx.request({
url: app.globalData.config.requrl + "",
data: data,
method: "GET",
success: function (res) {
res = res.data;
if (res.code == 1) {
wx.setStorageSync(app.globalData.config.storage.ukey, res.result.ukey);
wx.setStorageSync(app.globalData.config.storage.ukeytime, new Date().getTime());
if (close) {
self.close();
}
}
},
fail: function (res) {
console.log(res);
},
complete: function () {
// 授權(quán)完成并獲取到用戶數(shù)據(jù)后镐侯,給頁面提供的回調(diào)
self.triggerEvent("callback");
}
})
},
// 校驗是否授權(quán)
judgeAuth: function () {
var self = this;
this.close();
wx.getSetting({
success(res) {
if (res.authSetting['scope.userInfo']) {
// 已經(jīng)授權(quán)關(guān)閉授權(quán)窗口
// 直接調(diào)用已授權(quán)的方法
self.close();
self.triggerEvent("callback");
return;
} else {
// 未授權(quán)去授權(quán)
self.checkSession();
}
}
})
},
checkSession: function () {
var self = this;
wx.checkSession({
success: function (res) {
//存在登錄狀態(tài)
self.checkUkey();
},
fail: function () {
//過期-重新登錄
self.login();
}
});
}
},
})
// index.wxml 用戶可將授權(quán)button放在需要的地方
<button class="auth_btn" type="default" open-type="getUserInfo" bindgetuserinfo="userauth">微信授權(quán)</button>
(2)request.js
修改之前接口未獲取到用戶信息的處理侦讨,刪除自動登錄代碼將回調(diào)直接返回.request.js在3中給出了源碼,這邊就不再展示了。我們修改req方法韵卤。
function req(data){
wx.request({
url: data.url,
data: ukeyData(data.data),
method: data.method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
header: {
'content-type':'application/x-www-form-urlencoded',
}, // 設(shè)置請求的 header
success: function(res){
// success
res = res.data;
if (res.code == ResultCode.ResultCodeSucc) {
if (typeof data.succ === "function") {
data.succ(res);
}
} else if (res.code == ResultCode.ResultCodeNoAccredit) {
// 用戶登錄失效骗污,將回調(diào)直接放回
if (typeof data.no_accredit === "function") {
data.no_accredit(res);
}
} else {
if (typeof data.error === "function") {
data.error(res);
}
}
},
fail:function(res){
if(typeof data.fail === "function"){
data.fail(res);
}
},
complete:function(res){
if(typeof data.complete === "function"){
data.complete(res);
}
}
})
}
(3)在page中使用comp-auto組件
1.在onShow方法中校驗授權(quán)情況,未授權(quán)展示授權(quán)組件沈条,讓用戶授權(quán)
/**
* 生命周期函數(shù)--監(jiān)聽頁面顯示
*/
onShow: function () {
this.selectComponent("#compauth").judgeAuth();
},
2.書寫授權(quán)組件回調(diào)需忿,在此方法中書寫只有授權(quán)才能調(diào)用的接口
callback: function () {
this.person_appointment_tap();
}
3.修改需要授權(quán)才能使用的接口
// 預(yù)約活動
person_appointment_tap: function (e) {
if (!this.data.isAppointment) {
var self = this;
request.post({
url: app.globalData.config.requrl + "/Invest/Wx/subscribeMsg",
data: {
formid: e.detail.formId,
activity_id: self.data.activity_id
},
succ: function (res) {
// 成功
wx.showModal({
title: '預(yù)約成功',
content: "我們會在活動開始前給您發(fā)送消息提醒,請您及時關(guān)注",
showCancel: false,
confirmColor: "#ffcf00",
success: function (res) {
}
})
self.setData({ isAppointment: true });
},
error: function (res) {
// 失敗
prompt.showModal(res.code == 2 ? "您已經(jīng)預(yù)約過了蜡歹!" : "預(yù)約失敗贴谎,請稍后再試!", null);
if (res.code == 2) {
self.setData({ isAppointment: true });
}
},
no_accredit: function (res) {
// 吊起授權(quán)組件登錄方法季稳,授權(quán)結(jié)束后會調(diào)用回調(diào)方法callback,再次請求該接口
self.selectComponent("#compauth").login();
}
})
}
}
4.在index.json中添加組件
{
"usingComponents": {
"compauth": "../../components/comp-auth/index"
}
}
5.在index.wxml中添加組件
<compauth id="compauth" bind:callback="callback"></compauth>
這樣優(yōu)化后可以完善授權(quán)機制但是也有很大的弊端澈魄。因為微信沒有頂層視圖景鼠,所以導(dǎo)致了我們必須在每個需要授權(quán)的頁面添加組件。如果再修改授權(quán)機制痹扇,每個頁面都要進行維護铛漓,會產(chǎn)生很多的冗余代碼,讓項目變得難以維護鲫构。希望微信小程序之后可以考慮添加頂層視圖浓恶,減少碼農(nóng)的痛苦。
本文將持續(xù)對授權(quán)機制的處理進行更新结笨,謝謝大家的觀看包晰,有建議或者疑問歡迎給我留言。