知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?
您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >
微信小程序登錄鑒權(quán)與獲取用戶信息
發(fā)表時間:2021-3-15
發(fā)布人:葵宇科技
瀏覽次數(shù):286
前言
在小程序中,與云開發(fā)相比,傳統(tǒng)的前后端開發(fā)在登錄鑒權(quán)的實(shí)現(xiàn)方面相對來說更加復(fù)雜,不僅需要前端和后端的交互,后端還需要與微信接口服務(wù)進(jìn)行交互,以完成整個鑒權(quán)流程:
整個流程簡單來說分為以下7步:
- 前端調(diào)用
wx.login()
獲取臨時登錄憑證code
,并回傳到開發(fā)者服務(wù)器。 - 服務(wù)器調(diào)用auth.code2Session換取用戶唯一標(biāo)識OpenID和會話密鑰session_key。
- 服務(wù)器端根據(jù)
OpenID
或session_key
生成自定義登錄態(tài)(可以理解為是token
),將token
響應(yīng)給前端。 - 前端將
token
存入Storage
中。 - 當(dāng)前端之后向后端發(fā)起請求時,就會帶上
token
。 - 后臺通過
token
(或者其他類型密鑰),解密獲取OpenID
,判斷是哪個用戶的行為,做出響應(yīng)的邏輯處理(比如操作數(shù)據(jù)庫等)。 - 后臺響應(yīng)數(shù)據(jù)給前端。
這個是小程序登錄的流程,但是小程序登錄和小程序獲取用戶信息并不是一回事。小程序登錄的API
是wx.login
,可以獲取用戶的openID
,openID
是用戶的唯一標(biāo)識,是比較隱私的數(shù)據(jù),一般不會返回給前端。小程序獲取用戶信息的API
是wx.getUserInfo
,它可以獲取用戶的一些基本信息,比如nickName
、avatarUrl
等。兩者不要弄混。
其實(shí)微信登錄一開始并不是這樣的,以往微信小程序在用戶沒有任何操作的情況下就會直接彈出授權(quán)的登錄方式,如果用戶點(diǎn)擊拒絕授權(quán),則無法使用小程序。按照微信官方對這個功能更新的解釋是:


因此,微信對開發(fā)的建議是:
- 當(dāng)用戶打開小程序時訪問第一個頁面時,先通過
wx.login
,獲取用戶openID
。這時無需彈框授權(quán),開發(fā)者拿到openID
可以建立自身的帳號ID
。 - 在第一步中,拿到
openID
后,判斷是新用戶還是老用戶。如果是老用戶,可以直接登錄;如果是新用戶,可先在小程序首頁展示你的信息服務(wù),讓用戶對這個小程序有大概的了解,再引導(dǎo)用戶進(jìn)行下一步的操作。 - 當(dāng)需要獲取用戶頭像昵稱的時候,對用戶展示一個登錄頁面,這個頁面只有一個最重要的操作,引導(dǎo)用戶進(jìn)行登錄。
小程序登錄
在上一節(jié)中有提到,小程序登錄可以分為7個步驟,下面就詳細(xì)講一下7個步驟具體是如何實(shí)現(xiàn)。
step1: 前端調(diào)用wx.login()
獲取臨時登錄憑證code
在項(xiàng)目中,我使用了Taro
框架,所以調(diào)用的API
對應(yīng)為Taro.login()
:
Taro.login({})
.then((res) => {
if (res.code) {
// 將code發(fā)送到后臺,以獲取token
getToken(res.code)
.then((res: any) => {
const { token, userExist } = res;
// 將token存儲到Storage中
Taro.setStorageSync('token', token);
// 如果是老用戶,獲取用戶信息
if(userExist) {
const { userInfo } = res;
Taro.setStorageSync('userInfo', userInfo);
}
})
.catch((err) => {
console.error(err);
});
} else {
console.log('登錄失敗! ' + res.errMsg);
}
})
.catch((err) => {
console.error(err);
});
復(fù)制代碼
step2: 服務(wù)端調(diào)用auth.code2Session換取openid和session_key
服務(wù)端調(diào)用外部接口需要使用egg.js
中的一個api
:this.ctx.curl
,因?yàn)槭钱惒秸埱?,所以需要加?code>await:
// app/controller/home.js
// login接口
async login() {
const { ctx } = this;
const { code } = ctx.request.body;
// 服務(wù)器根據(jù)客戶端傳來的code向微信接口服務(wù)獲取session_key和openid
const res = await ctx.curl(
`https://api.weixin.qq.com/sns/jscode2session?
appid=wx6936c18b38186cf3&secret=d11f77fb7d5a959b6ba46c30dbd4da95&js_code=${code}&grant_type=authorization_code`,
{
dataType: 'json',
}
);
const { openid } = res.data; // 獲取到openid
}
復(fù)制代碼
step3: 根據(jù)openid
生成自定義登錄態(tài)token,響應(yīng)給前端
因?yàn)?code>openid是用戶的唯一標(biāo)識,根據(jù)它生成token,響應(yīng)給前端后,前端每次發(fā)請求帶上token,后臺解密請求中的token,獲取到openid
,便能識別這是哪個用戶的請求行為。
這里我們使用jwt
來生成自定義登錄態(tài)token
,使用jwt-simple
庫來生成jwt
:
const jwt = require('jwt-simple');
const SECRET = 'zhuoran'; // 自定義
async login() {
...
const { openid } = res.data; // 獲取到openid
// 根據(jù)用戶的openid生成token
const token = jwt.encode(openid, SECRET);
// 將token返回
ctx.body = {
token: token,
...
};
...
}
復(fù)制代碼
step4: 前端將token存入storage
// 將token存儲到Storage中
Taro.setStorageSync('token', token);
復(fù)制代碼
并在每次請求時帶上token
,將token
放在請求頭的Authorization
字段里面:
const option = {
url: BASE_URL + url,
data: data,
method: method,
header: {
'content-type': contentType,
Authorization: Taro.getStorageSync('token'),
},
};
Taro.request(option);
復(fù)制代碼
這樣前端之后向后端發(fā)起請求,都會帶上token。
step5: 后臺解密token獲取openid
在后臺解密token獲取openid之后,便能知道這是哪個用戶的請求,執(zhí)行響應(yīng)的操作:
async request() {
const { ctx } = this;
// 從請求頭的authorization字段獲取token
const token = ctx.get('authorization');
// 對token進(jìn)行解密獲取其中的openid
const openid = jwt.decode(token, SECRET);
// 根據(jù)openid查找用戶信息
const res = await ctx.model.User.findAll({
where: {
openid: openid,
},
});
// 之后注意要將openid屬性去掉,私密屬性不傳回給客戶端
ctx.body = res;
}
復(fù)制代碼
額外需要注意的點(diǎn)
保存用戶登錄態(tài),一直以來都有兩種解決方案:前端保存和后端保存。
- 后端保存:在后端設(shè)定并存儲當(dāng)前token的過期時間,定期通知小程序前端重新登錄
- 前端保存:因?yàn)閟ession_key存在時效性(因?yàn)橥ㄟ^session_key我們可以查看敏感信息,所以必定會有一定的時效性),而小程序前端可以通過
wx.checkSession()
來檢查session_key
是否過期。我們可以自定義登錄態(tài),并考慮以session_key有效期作為自身登錄態(tài)有效期(也就是以session_key的到期時間作為自定義登錄態(tài)的到期時間,兩者實(shí)際上并沒有實(shí)質(zhì)聯(lián)系)。這個也是小程序文檔中推薦的方法。
因此,在項(xiàng)目中token是會過期的,一段時間沒有使用就會導(dǎo)致它過期。那怎么去檢驗(yàn)token是否過期,然后去更新它呢?
這里就需要用到前端攔截器和后端中間件。前端攔截器用來判斷token
是否過期,后端中間件用于判斷請求是否攜帶token以及token是否有效。
前端攔截器
這里會使用到Taro
攔截器API
:Taro.addInterceptor(callback)
。攔截器允許我們在請求發(fā)出前或發(fā)出后做一些額外操作。
例如:
const interceptor = function (chain) {
// 攔截請求發(fā)出前做一些額外操作
const requestParams = chain.requestParams
const { method, data, url } = requestParams
console.log(`http ${method || 'GET'} --> ${url} data: `, data)
return chain.proceed(requestParams)
.then(res => {
// 攔截請求發(fā)出后做一些額外操作
console.log(`http <-- ${url} result:`, res)
return res
})
}
Taro.addInterceptor(interceptor)
復(fù)制代碼
所以,我們可以將checkSession
步驟寫在前端攔截器里,在請求每次發(fā)出之前判斷session_key
是否有過期,如果過期了,則重新調(diào)用login
方法,更新token,如果沒有過期,則正常發(fā)起請求:
// login方法
const login = () => {
Taro.login({})
.then((res) => {
if (res.code) {
console.log('code為' + res.code);
// 將code和userInfo發(fā)送到后臺,以獲取token
getToken(res.code)
.then((token) => {
console.log('獲取token');
console.log(token);
// 將token存儲到Storage中
Taro.setStorageSync('token', token);
})
.catch((err) => {
console.error(err);
});
} else {
console.log('登錄失敗! ' + res.errMsg);
}
})
.catch((err) => {
console.error(err);
});
};
// 自定義攔截器
const customInterceptor = (chain) => {
const requestParams = chain.requestParams;
// 獲取token
const loginFlag = Taro.getStorageSync('token');
// 檢查session是否過期
Taro.checkSession({})
.then((res) => {
console.log(res);
console.log('session沒過期,不用重新登錄');
console.log('token為' + loginFlag);
})
.catch((err) => {
console.log(err);
console.log('session已過期,要重新登錄');
// 重新登錄
login();
});
return chain.proceed(requestParams).then((res) => {
// 只要請求成功,不管返回什么狀態(tài)碼,都走這個回調(diào)
switch (res.statusCode) {
case HTTP_STATUS.NOT_FOUND:
return Promise.reject('請求資源不存在');
case HTTP_STATUS.BAD_GATEWAY:
return Promise.reject('服務(wù)端出現(xiàn)了問題');
case HTTP_STATUS.FORBIDDEN: {
Taro.setStorageSync('Authorization', '');
// pageToLogin();
// TODO 根據(jù)自身業(yè)務(wù)修改
return Promise.reject('沒有權(quán)限訪問');
}
case HTTP_STATUS.AUTHENTICATE: {
Taro.setStorageSync('Authorization', '');
// pageToLogin();
return Promise.reject('需要鑒權(quán)');
}
case HTTP_STATUS.SUCCESS:
return res.data;
}
});
};
// Taro 提供了兩個內(nèi)置攔截器
// logInterceptor - 用于打印請求的相關(guān)信息
// timeoutInterceptor - 在請求超時時拋出錯誤。
const interceptors = [
customInterceptor,
Taro.interceptors.logInterceptor,
];
export default interceptors;
復(fù)制代碼
后端中間件
正如之前所述,后端中間件的作用是用戶判斷請求是否攜帶token以及token是否有效的。我們在egg Node.js后端項(xiàng)目中定義校驗(yàn)token的中間件:
// app/middleware/auth.js
let jwt = require('jwt-simple');
const SECRET = 'zhuoran';
module.exports = (options) => {
return async function auth(ctx, next) {
const token = ctx.get('authorization');
if (token) {
console.log('請求帶有token');
try {
const openid = jwt.decode(token, SECRET);
await next();
} catch (err) {
ctx.body = {
code: 401,
msg: 'token有誤',
};
}
} else {
console.log('請求沒有帶token');
ctx.body = {
code: 401,
msg: '您沒有登錄',
};
}
};
};
復(fù)制代碼
因?yàn)樾〕绦蛑?,有些功能不?qiáng)制要求用戶登錄之后才能使用,所以有些請求操作不需要后臺校驗(yàn)是否有token,那么這個auth.js
中間件就不能夠全局配置,而是放在需要校驗(yàn)token的路由下:
module.exports = (app) => {
const { router, controller, middleware } = app;
const auth = middleware.auth();
router.get('/', controller.home.index);
router.get('/request', auth, controller.home.request); // 將middleware放在中間
router.post('/login', controller.home.login);
router.post('/userInfo', controller.home.userInfo);
};
復(fù)制代碼
登錄鑒權(quán)及獲得用戶信息流程梳理
登錄:
獲取用戶信息:
總結(jié)
相對于云開發(fā)來說,傳統(tǒng)的前后端開發(fā)在登錄鑒權(quán)方面復(fù)雜許多,涉及多端交互,以上總結(jié)也有許多要改進(jìn)的地方??偠灾?,將邏輯理順之后,寫代碼就比較簡單了。
在登錄鑒權(quán)過程中比較重要的點(diǎn):
- 知道什么是用戶的唯一標(biāo)識,并根據(jù)它生成token
- 對token進(jìn)行校驗(yàn),判斷其是否過期,是否準(zhǔn)確
- 通過畫流程圖等方式理清各個過程的邏輯
參考
使用Nodejs實(shí)現(xiàn)jwt原理
手把手教會你小程序登錄鑒權(quán)
小程序登錄那些事
小程序:授權(quán)、登錄、session_key、unionId
微信小程序wx.getUserInfo授權(quán)獲取用戶信息(頭像、昵稱)
前后端分離項(xiàng)目,token過期,重新登錄和刷新token的問題
egg中間件匹配路由
Taro微信小程序登錄
Button組件onGetUserInfo未執(zhí)行
相關(guān)案例查看更多
相關(guān)閱讀
- 云南做網(wǎng)站
- web服務(wù)
- 網(wǎng)站建設(shè)價格
- 商標(biāo)注冊
- 網(wǎng)絡(luò)公司報價
- 報廢車拆解回收管理系統(tǒng)
- 網(wǎng)絡(luò)公司聯(lián)系方式
- 小程序開發(fā)平臺前十名
- 云南網(wǎng)站建設(shè)服務(wù)公司
- 大理小程序開發(fā)
- 云南網(wǎng)站制作
- 云南網(wǎng)站建設(shè)方法
- 怎么做網(wǎng)站
- 網(wǎng)站建設(shè)報價
- 網(wǎng)站開發(fā)公司哪家好
- 云南旅游網(wǎng)站建設(shè)
- 云南網(wǎng)站維護(hù)
- 汽車報廢回收管理系統(tǒng)
- 云南網(wǎng)站建設(shè)首頁
- 云南小程序開發(fā)課程
- 江蘇小程序開發(fā)
- 昆明小程序定制開發(fā)
- 云南小程序開發(fā)哪家好
- 保險網(wǎng)站建設(shè)公司
- vue開發(fā)小程序
- 云南網(wǎng)站建設(shè)首選
- 網(wǎng)絡(luò)營銷
- 汽車報廢管理系統(tǒng)
- 開通微信小程序被騙
- web開發(fā)