時(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í)光飛逝的幾年...
公眾號調(diào)用各接口時(shí)都需使用access_token。開發(fā)者需要進(jìn)行妥善保存。access_token的存儲至少要保留512個(gè)字符空間。access_token的有效期目前為2個(gè)小時(shí),需定時(shí)刷新,重復(fù)獲取將導(dǎo)致上次獲取的access_token失效我們從中可以得到如下幾條信息:
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ì)列么。
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ù), 我們有資格去請求用戶資料了。獲取關(guān)注粉絲基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證
網(wǎng)頁授權(quán)獲取用戶基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證
獲取關(guān)注粉絲基本信息: 已獲得
每日上限:500000 次
網(wǎng)頁授權(quán)獲取用戶基本信息: 未獲得
獲得條件:必須是服務(wù)號+必須通過微信認(rèn)證
獲取關(guān)注粉絲基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證
網(wǎng)頁授權(quán)獲取用戶基本信息: 未獲得
獲得條件:必須通過微信認(rèn)證
獲取關(guān)注粉絲基本信息: 已獲得
每日上限:500000 次
網(wǎng)頁授權(quán)獲取用戶基本信息: 已獲得轟轟烈烈的 8 種情況,就問你怕不怕。
每日上限:無上限
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)頁中用戶資料的獲取就是另外一回事了。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)就行啊......// 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ù)...}
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)天下:作者: Scott
鏈接:https://www.imooc.com/article/19204
來源:慕課網(wǎng)
本文原創(chuàng)發(fā)布于慕課網(wǎng) ,轉(zhuǎn)載請注明出處,謝謝合作!
關(guān)鍵詞:程序,用戶,獲取,資料,公眾
客戶&案例
營銷資訊
關(guān)于我們
微信公眾號
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。