這幾年的社交,是微信的社交
這幾年的微信開發(fā),是基于微信公眾號的開發(fā)
這幾年的公眾號還沒折騰明白,小程序" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運(yùn)營 > 一鍋端掉微信公眾號-小程序的用戶資料獲取

一鍋端掉微信公眾號-小程序的用戶資料獲取

時(shí)間:2022-08-06 20:03:01 | 來源:網(wǎng)站運(yùn)營

時(shí)間:2022-08-06 20:03:01 來源:網(wǎng)站運(yùn)營

本篇手記,旨在解決微信跨產(chǎn)品鏈路中的用戶資料種種痛點(diǎn),業(yè)務(wù)場景解惑與技術(shù)實(shí)現(xiàn)細(xì)節(jié)并存,約 4000 字,請耐心閱讀。

這幾年的社交,是微信的社交
這幾年的微信開發(fā),是基于微信公眾號的開發(fā)
這幾年的公眾號還沒折騰明白,小程序便迫不及待撲面而來
這幾年的掙扎開發(fā)歷程,總是漫不經(jīng)心卻時(shí)光飛逝的幾年...

昨天的舊票據(jù)還能否登上你的破船

我想,任何一個(gè)經(jīng)歷過微信公眾號開發(fā)的同仁,肯定有過罵娘的夜晚,剛吭吭哧哧搞定內(nèi)網(wǎng)端口映射到外網(wǎng)域名,調(diào)通后臺 URL 接入認(rèn)證,就掉入到 access_token 的坑,有基礎(chǔ)版的 access_token,又有網(wǎng)頁版的 access_token,有訂閱號的 token 權(quán)限,又有服務(wù)號的 token 權(quán)限,有認(rèn)證過的訂閱號的 token 權(quán)限,又有認(rèn)證過的服務(wù)號的 token 權(quán)限,有一些每天限制調(diào)用次數(shù),有一些不限,有一些可以刷新獲取,有一些則不能,最怕最怕公司產(chǎn)品既有訂閱號,又有服務(wù)號,還有小網(wǎng)站,于是又摻和進(jìn)來了 UnionID,噩夢不醒...

Scott 決定從微信第一大坑入手,徹底弄清楚通過 token 獲取用戶資料的場景和流程。

8 種不同的用戶資料獲取場景

別怕,只有 8 種而已。先搞定 access_token,我們再把魔爪伸向用戶。

進(jìn)入微信公眾平臺技術(shù)文檔,映入眼簾的是這樣一段話:

公眾號調(diào)用各接口時(shí)都需使用access_token。開發(fā)者需要進(jìn)行妥善保存。access_token的存儲至少要保留512個(gè)字符空間。access_token的有效期目前為2個(gè)小時(shí),需定時(shí)刷新,重復(fù)獲取將導(dǎo)致上次獲取的access_token失效
我們從中可以得到如下幾條信息:

基于這幾個(gè)信息,該祭代碼了:

const API = '微信全局 access_token API'export async getToken() { let data = await fetchTokenFromDbOrAPI() let now = (new Date().getTime()) if (data.expires > now / 1000) { return data } // 票據(jù)過期 重新獲取 data = await updateToken() // 設(shè)置到期時(shí)間 now = (new Date().getTime()) data.expires = now / 1000 + data.expires_in // 入庫或同步給某個(gè)服務(wù) await saveTokenToDbOrAPI(data) return data}export async updateToken() { const data = await request(API) return data}官方文檔中還有這樣一句話:

在刷新過程中,中控服務(wù)器對外輸出的依然是老access_token,此時(shí)公眾平臺后臺會保證在刷新短時(shí)間內(nèi),新老access_token都可用,這保證了第三方業(yè)務(wù)的平滑過渡
保證在刷新短時(shí)間內(nèi) 是一個(gè)什么概念呢,刷新要多久,是 100 毫秒,還是 2 秒?刷新動作發(fā)起的時(shí)時(shí)候到收到請求存入到數(shù)據(jù)庫,到能對外提供服務(wù),這中間如果有其他的用戶請求觸發(fā)了再次刷新,那么需要在服務(wù)器端做已發(fā)出刷新動作的統(tǒng)計(jì)么,需要加鎖 hold 住攔截當(dāng)前的刷新動作么,需要一直等到上一個(gè)刷新成功返回且存入數(shù)據(jù)庫再清空刷新隊(duì)列么。

為了不心煩頭疼,通常我們這么干,就是加大提前量,在過期前 10 分鐘 就定時(shí)主動刷新,或者對于產(chǎn)品容錯要求不高的項(xiàng)目,如果用戶觸發(fā)了請求,只要在 10 分鐘時(shí)差內(nèi),就果斷刷新,反正一天的請求量是 2000 次,對于 10 分鐘的時(shí)間差也足夠用了,該祭代碼了:

export async getToken() { let data = await fetchTokenFromDbOrAPI() let now = (new Date().getTime()) if (data.expires > now / 1000) { return data } // 票據(jù)過期 重新獲取 data = await updateToken() // 設(shè)置到期時(shí)間,并縮短 10 分鐘 now = (new Date().getTime()) - 600 * 1000 data.expires = now / 1000 + data.expires_in // 入庫或同步給某個(gè)服務(wù) await saveTokenToDbOrAPI(data) return data}好,我們搞定了 公眾號的全局唯一接口調(diào)用憑據(jù), 我們有資格去請求用戶資料了。

等等...UnionID 是怎么回事?OpenID 怎么辦?

先別慌,我們先把訂閱號服務(wù)號的邊界搞清楚,這就是我說的 8 種用戶資料場景。

第一種 未認(rèn)證訂閱號無獲取用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

獲取關(guān)注粉絲基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證

第二種 未認(rèn)證訂閱號無獲取網(wǎng)頁授權(quán)用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

網(wǎng)頁授權(quán)獲取用戶基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證

第三種 已認(rèn)證訂閱號有獲取用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

獲取關(guān)注粉絲基本信息: 已獲得
每日上限:500000 次

第四種 已認(rèn)證訂閱號無獲取網(wǎng)頁授權(quán)用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

網(wǎng)頁授權(quán)獲取用戶基本信息: 未獲得
獲得條件:必須是服務(wù)號+必須通過微信認(rèn)證

第五種 未認(rèn)證服務(wù)號無獲取用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

獲取關(guān)注粉絲基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證

第六種 未認(rèn)證服務(wù)號無獲取網(wǎng)頁授權(quán)用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

網(wǎng)頁授權(quán)獲取用戶基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證

第七種 已認(rèn)證服務(wù)號有獲取用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

獲取關(guān)注粉絲基本信息: 已獲得
每日上限:500000 次

第八種 已認(rèn)證服務(wù)號有獲取網(wǎng)頁授權(quán)用戶信息權(quán)限

請登錄到公眾號后臺,瞪大雙眼看:

網(wǎng)頁授權(quán)獲取用戶基本信息: 已獲得
每日上限:無上限
轟轟烈烈的 8 種情況,就問你怕不怕。

我們總結(jié)一下:

并且對于網(wǎng)頁授權(quán)讀取用戶資料,是認(rèn)證服務(wù)號的特權(quán),獲取方式也是非同凡響,我們后面來談。

獲取關(guān)注粉絲用戶信息

上面我們拿到了 公眾號的全局唯一接口調(diào)用憑據(jù) access_token,每一次用戶主動發(fā)的消息,都會發(fā)過來一個(gè) XML 數(shù)據(jù)包,解析這個(gè)數(shù)據(jù)包后,就能拿到里面的 FromUserName,大概長這個(gè)樣子:

const message = { ToUserName: 'gh_c69edc91fe37', FromUserName: 'oW4nAvpSgoLKfVDdtK_VvGutDako', CreateTime: '1500037104', MsgType: 'text', Content: 'uu', MsgId: '6442610305031245235'}拿到后,無論在認(rèn)證過的訂閱號還是認(rèn)證過的服務(wù)號中,就可以獲取關(guān)注公眾號的粉絲資料了,祭出代碼:

const userAPI = '微信用戶基本信息 API'export async getUserInfo(openID) { const data = await getToken() const token = data.access_token const openID = message.FromUserName const url = `${userAPI}?access_token=${token}&openid=${openID}` const userData = await request(userAPI) return userData}似乎一切順風(fēng)順?biāo)鞘且驗(yàn)殛P(guān)注過公眾號的粉絲,在向我們推送消息時(shí)候,消息中已經(jīng)包含了 openID 了,所以拼接個(gè) url 請求就好了,但是網(wǎng)頁中用戶資料的獲取就是另外一回事了。

扎心的網(wǎng)頁 OAuth 2.0 授權(quán)

我們能搞定粉絲信息,是因?yàn)槲覀冊诠娞柕膬?nèi)部系統(tǒng)中才有這個(gè)權(quán)限,脫離了公眾號,游走在微信其他地方,就得依賴另外一套生存法則了,并且這套法則只對認(rèn)證服務(wù)號生效,如果你的產(chǎn)品是訂閱號,你需求方非讓你在網(wǎng)頁中照搬上面的功能,你可以把我之前列的第四種情況甩他一臉。

對于網(wǎng)頁獲取用戶信息,我們需要先搞清楚什么是 OAuth 2.0,這方面文章有很多,大家可以自行補(bǔ)課,我把微信里的授權(quán)流程簡單描述下:

不好意思,不小心又湊出來個(gè) 8 步棋..恩恩..網(wǎng)址 B..噗噗..openID...

只可惜,這個(gè) openID 還是那個(gè) openID,而 access_token 卻已乾坤大魔移,該祭代碼了:

const userSNSAPI = '微信 SNS 用戶資料 API'const authAPI = '微信 OAuth 2.0 API'const tokenAPI = '微信網(wǎng)頁授權(quán) access_token API'// 此票據(jù)并不是前面的 公眾號的全局唯一接口調(diào)用憑據(jù)export async getToken(code) { let data = await fetchTokenFromDbOrAPI() let now = (new Date().getTime()) if (data.expires > now / 1000) { return data } // 票據(jù)過期 重新獲取 data = await updateToken() // 設(shè)置到期時(shí)間,并縮短 10 分鐘 now = (new Date().getTime()) - 600 * 1000 data.expires = now / 1000 + data.expires_in // 入庫或同步給某個(gè)服務(wù) await saveTokenToDbOrAPI(data) return data}// 拼接一個(gè)微信域名的 URL B,參數(shù)放上我們真正想要跳轉(zhuǎn)的 URL C// 用戶打開 URL B,再點(diǎn)擊授權(quán)按鈕(微信自動展現(xiàn)不需我們關(guān)心),跳到 URL Cexport function oAuthURL(scope, redirect, state) { const url = encodeURIComponent(redirect) return `${authAPI}?appid=${ID}&redirect_uri=${url}&response_type=code&&scope=${scope}&state=${state}#wechat_redirect`}// http://x.o/redirect/a// 用戶進(jìn)入 URL A,被你偷偷換成 Bexport async visitPageA(ctx, next) { const scope = 'snsapi_userinfo' const redirect = 'http://x.o/redirect/c' const state = 'abc' const url = oAuthURL(scope, redirect, state) ctx.redirect(url)}// http://x.o/redirect/c?code=xo&state=abc// 用戶進(jìn)入 URL C,被你偷偷拿到 code 換數(shù)據(jù)export async visitPageC(ctx, next) { // 拿到 state 就拿到了跳轉(zhuǎn)之前用戶的所在狀態(tài) // const state = ctx.query.state const code = ctx.query.code const data = await getToken(code) const openID = data.openid const url = `${userSNSAPI}?access_token=${token}&openid=${openID}` const userData = await request(url) // 拿到 userData 做其他業(yè)務(wù)...}好,總算是能拿到用戶信息了,松了一口氣,結(jié)果產(chǎn)品經(jīng)理跑過來,氣喘吁吁的說,兄弟兄弟,快醒醒,咱們要上小程序了,這是需求清單,照著公眾號網(wǎng)頁 App 的功能實(shí)現(xiàn)就行啊......

此處省略 33 小時(shí)的狂吐槽和自我心理掙扎....

沒事,甩甩頭,再次踏上開發(fā)小程序的戰(zhàn)場。

全平臺統(tǒng)一用戶信息

經(jīng)過一番文檔各種比對,知道了,可以把小程序和公眾號綁定到微信開放平臺上來,這樣的話,獲取用戶信息的時(shí)候,會拿到一個(gè) unionID,這個(gè) unionID 跟 openID 一樣,可以獲取用戶的資料,不同的的是,unionID 對于同一個(gè)用戶,無論他是在小程序里面,還是在公眾號里面,他的 unionID 都是相同的,這樣就可以通過 unionID 來識別出,通過不同平臺訪問我們服務(wù)的人,自然能統(tǒng)一掉他的賬號體系。

這樣一個(gè)大招,代碼卻并不需要做多少改動,unionID 可以直接當(dāng)做 openid 來用,從前用 openid 請求用戶信息的地方,現(xiàn)在用 openid=unionID 同樣可以拿到,直接祭出代碼:

// http://x.o/redirect/c?code=xo&state=abc// 用戶進(jìn)入 URL C,被你偷偷拿到 code 換數(shù)據(jù)export async visitPageC(ctx, next) { // 拿到 state 就拿到了跳轉(zhuǎn)之前用戶的所在狀態(tài) // const state = ctx.query.state const code = ctx.query.code const data = await getToken(code) // openid 可以獲取后,跟既有數(shù)據(jù)庫里的 openid 比對 // 比對上,就把之前的 openid 邏輯逐步干掉,替換成 unionid // const openID = data.openid // 從此拿 unionID 來請求用戶信息即可 const unionID = data.unionid const url = `${userSNSAPI}?access_token=${token}&openid=${unionID}` const userData = await request(url) // 拿到 userData 做其他業(yè)務(wù)...}

小程序迎刃而解

上面我們通過 unionID 拿到了用戶信息,小程序里面,代碼就可以這樣搞了:

export const getUserByCode = async code => { const options = { uri: 'https://api.weixin.qq.com/sns/jscode2session', qs: { appid: 'appid', secret: 'secret', js_code: code, grant_type: 'authorization_code' }, json: true } const userData = await request(options) return userData}// 收到小程序端發(fā)過來的請求,解析 UserInfoexport async getMinaUer(ctx, next) { const userInfo = ctx.query.userInfo const code = ctx.query.code const userData = await getUserByCode(code) const wxBizDataCrypt = new WXBizDataCrypt(userData.session_key) const decryptData = wxBizDataCrypt.decryptData(userInfo.encryptedData, userInfo.iv) // 解析出來 unionid const unionid = wxBizDataCrypt.unionid // ...}于是宣告一統(tǒng)天下:

以上的三個(gè) openID 是不同的 openID,但是 unionID 卻是同一個(gè)。

過渡期的用戶存儲

從前只有公眾號的時(shí)候,獲取用戶資料保存信息,都是通過 openID 一網(wǎng)打盡,而隨著業(yè)務(wù)的覆蓋面,openID 切換到了 unionID,但是一開始可能是沒有 unionID 權(quán)限的,或者不確定將來會不會切換到 unionID,那么可以在初次數(shù)據(jù)建模的時(shí)候,把 openID 保存一下,存成一個(gè)數(shù)組,等到將來有了 unionID 后,再逐步來篩選替換即可。

如果涉及到 PC 端的微信用戶掃碼登錄,那么整個(gè)場景又會略有不同,限于篇幅,我們下一次來探討,文章有不當(dāng)紕漏支持,請不吝指出。




作者: Scott
鏈接:https://www.imooc.com/article/19204
來源:慕課網(wǎng)
本文原創(chuàng)發(fā)布于慕課網(wǎng) ,轉(zhuǎn)載請注明出處,謝謝合作!

關(guān)鍵詞:程序,用戶,獲取,資料,公眾

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉