背景
公司為客戶開發(fā)微信公眾號(hào)相關(guān)服務(wù)時(shí),有時(shí)未能準(zhǔn)備好公眾號(hào),所以需要使用公司的公眾號(hào),但是大家都知道微信網(wǎng)頁(yè)授權(quán)域名最多只支持兩個(gè),這就造成了如果有多個(gè)項(xiàng)目需要同時(shí)開發(fā)時(shí)產(chǎn)生了如下問(wèn)題:
- 網(wǎng)頁(yè)授權(quán)地址不夠用
- 公眾號(hào)不夠用
- 某功能比如微信快捷登錄突然失效(授權(quán)地址被改掉)
解決方案
關(guān)于域名占用的問(wèn)題,其實(shí)在github
上已經(jīng)有現(xiàn)成的方法了,可以實(shí)現(xiàn)多域名的授權(quán),而且實(shí)現(xiàn)內(nèi)容也比較簡(jiǎn)單,就是一個(gè)粗暴的靜態(tài)html
文件去處理授權(quán)用的參數(shù)。博主之前曾經(jīng)做過(guò)一個(gè)網(wǎng)頁(yè)授權(quán)掃碼登錄的Demo
就用到了這個(gè)靜態(tài)文件。
正常情況下如果用到了網(wǎng)頁(yè)授權(quán)獲取用戶信息,一般是需要一臺(tái)服務(wù)器一個(gè)備案過(guò)的域名的,那么如果沒(méi)有服務(wù)器改咋整呢?
云函數(shù)很巧妙地解決了這個(gè)問(wèn)題,我們只需要一個(gè)自己的域名(不用其實(shí)也可以)就可以通過(guò)云函數(shù)來(lái)托管這個(gè)授權(quán)用的文件來(lái)實(shí)現(xiàn)通用的授權(quán)服務(wù)。
下面我們來(lái)看一下如何去做這么一個(gè)簡(jiǎn)易的基礎(chǔ)服務(wù)。
需求分析
首先我們知道配置網(wǎng)頁(yè)授權(quán)域名的時(shí)候需要在公眾號(hào)添加這個(gè)域名,要求我們?cè)诜?wù)器上上傳一個(gè)驗(yàn)證文件,并且這個(gè)文件要掛在根目錄下才可以訪問(wèn)到,這就要求我們?cè)黾右粋€(gè)文件上傳的功能。
這種情況下云函數(shù)就需要具備如下能力:
- 靜態(tài)文件托管
txt
驗(yàn)證文件上傳
顯然自己手動(dòng)從零編寫一個(gè)云函數(shù)就有些繁瑣了,不過(guò)還有我們有內(nèi)置應(yīng)用模板幫助簡(jiǎn)化工作量。
實(shí)現(xiàn)步驟
應(yīng)用創(chuàng)建
在云函數(shù)的后臺(tái)直接創(chuàng)建應(yīng)用,使用koa模板。
應(yīng)用修改
應(yīng)用創(chuàng)建好之后會(huì)在云函數(shù)列表里出現(xiàn)名為koa-starter
的函數(shù),我們需要修改這個(gè)函數(shù)的代碼。
應(yīng)用模板的源碼在github上就可以獲取->koa-starter。
這里講解一下幾個(gè)核心修改的實(shí)現(xiàn)吧:
app.js
內(nèi)增加文件上傳的支持,小文件是可以直接上傳的。
app.use( koaBody({ multipart: true, formidable: { multipart: true, maxFileSize: 400 * 1024 * 1024 // 設(shè)置上傳文件大小最大限制,默認(rèn)4M } }))
- 然后增加一些全局的處理
// 全局異常處理app.use(async (ctx, next) => { try { await next() } catch (err) { ctx.body = { code: -1, data: ctx.data, message: ctx.msg || err.message || 服務(wù)開小差了,請(qǐng)稍后再試, etime: Date.now() } }})// pretty json resultapp.use(async (ctx, next) => { await next() if (ctx.data) { ctx.set(Content-Type, application/json) ctx.body = { code: ctx.code || 0, data: ctx.data, message: ctx.msg || success, etime: Date.now() } }})
- 在
routes/index.js
中增加上傳文件的路由處理
router.post("/uptxt", async (ctx, next) => { if (!ctx.request.files) { ctx.data = 未選擇文件 await next() return } // 獲取上傳文件 let file = ctx.request.files.file // 創(chuàng)建可讀流 let reader = fs.createReadStream(file.path) let filePath = `/tmp/${file.name}` // 創(chuàng)建可寫流 const upStream = fs.createWriteStream(filePath) await reader.pipe(upStream); ctx.data = file.name ctx.code = 0 ctx.msg = 上傳驗(yàn)證文件成功 await next()});
- 前端的模板目錄
views
下面增加兩份頁(yè)面代碼。auth.html
文件,用于授權(quán)處理,代碼參考。up.html
文件,用于文件上傳。
文件上傳功能我們需要注意一點(diǎn):
- 云函數(shù)在執(zhí)行過(guò)程中,都擁有一塊500MB的臨時(shí)磁盤空間 /tmp,用戶可以在執(zhí)行代碼時(shí)對(duì)該空間進(jìn)行一些讀寫操作,也可以創(chuàng)建子目錄,但這部分?jǐn)?shù)據(jù)在函數(shù)執(zhí)行完成后不會(huì)保留。
因?yàn)樾枰蟼饕粋€(gè)驗(yàn)證文件所以這個(gè)臨時(shí)目錄自然會(huì)有這個(gè)txt文件,但是微信需要驗(yàn)證這個(gè)文件的有效性,所以這就意味著tmp目錄下的東西需要被我們?cè)L問(wèn)到,那該怎么辦?
解決辦法當(dāng)然是有的,那就是手動(dòng)修改靜態(tài)資源目錄為tmp。
修改app.js文件:
app.use(require(koa-static)(/tmp))
這樣在上傳之后就可以直接訪問(wèn)到了。
- 首頁(yè)及上傳頁(yè)的路由處理:
router.get("/", async (ctx, next) => { await ctx.render("index");});router.get("/up", async (ctx, next) => { await ctx.render("up");});
- 授權(quán)頁(yè)的訪問(wèn)路由處理:
router.get("/auth.html", async (ctx, next) => { await ctx.render("auth");});
- 保存代碼并配置訪問(wèn)服務(wù)。
前端接入
vue項(xiàng)目為例
插件引入:
在項(xiàng)目中加入生成回調(diào)地址的wechatAuth.js
文件。
部分代碼參考:
// 引入插件import wechatAuth from @/plugins/wechatAuth// 設(shè)置APPIDwechatAuth.setAppId(process.env.VUE_APP_WECHAT_APPID)// 使用插件生成授權(quán)回調(diào)地址wechatAuth.redirect_uri = processUrl()/** * 處理url鏈接 * @returns {string} */function processUrl() { // 本地環(huán)境拿code if (process.env.NODE_ENV === development) { // 中間授權(quán)頁(yè)地址 return `${process.env.VUE_APP_WECHAT_AUTH_URL}?backUrl=${window.location.href}` } const url = window.location.href // 解決多次登錄url添加重復(fù)的code與state問(wèn)題 const urlParams = qs.parse(url.split(?)[1]) let redirectUrl = url if (urlParams.code && urlParams.state) { delete urlParams.code delete urlParams.state const query = qs.stringify(urlParams) if (query.length) { redirectUrl = `${url.split(?)[0]}?${query}` } else { redirectUrl = `${url.split(?)[0]}` } } return redirectUrl}
環(huán)境變量配置:
#appid 可填入申請(qǐng)的測(cè)試公眾號(hào)id或者其它準(zhǔn)備好的IDVUE_APP_WECHAT_APPID=#authUrl 網(wǎng)頁(yè)授權(quán)中間頁(yè)VUE_APP_WECHAT_AUTH_URL=云函數(shù)http訪問(wèn)服務(wù)地址/auth.html
整個(gè)授權(quán)服務(wù)的流程可概括為下圖:
因?yàn)槲覀冎皇前勋@取微信授權(quán)code的過(guò)程統(tǒng)一放到了云函數(shù)去處理,所以多個(gè)項(xiàng)目在調(diào)試時(shí)都可以使用同一個(gè)地址,減少了資源占用,不失為一個(gè)可用方案。
我們僅需要一個(gè)云函數(shù)就可以實(shí)現(xiàn)微信授權(quán)的本地調(diào)試以及幾個(gè)項(xiàng)目幾個(gè)公眾號(hào)共用一個(gè)授權(quán)服務(wù),免去獨(dú)立域名、獨(dú)立服務(wù)器的煩惱。
服務(wù)Demo演示
這里提供了一個(gè)云函數(shù)網(wǎng)頁(yè)授權(quán)服務(wù)的Demo地址:
http://cloud.xuedingmiao.com/
參考資料
本文作者:薛定喵君
原文地址:http://xuedingmiao.com/blog/scf_wx_page_auth_service.html