【技术种草】巧用云函数打造微信网页授权公用服务

背景

公司为客户开发微信公众号相关服务时,有时未能准备好公众号,所以需要使用公司的公众号,但是大家都知道微信网页授权域名最多只支持两个,这就造成了如果有多个项目需要同时开发时产生了如下问题:

  • 网页授权地址不够用
  • 公众号不够用
  • 功能比如微信快捷登录突然失效(授权地址被改掉)

解决方

关于域名占用的问题,其实在github上已经有现成的方法了,可以实现多域名的授权,而且实现内容也比较简单,就是一个粗暴的静态html文件去处理授权用的参数。博主之前曾经做过一个网页授权扫码登录Demo就用到了这个静态文件

正常情况下如果用到了网页授权获取用户信息,一般是需要一台服务器一个备案过的域名的,那么如果没有服务器改咋整呢?

云函数很巧妙地解决了这个问题,我们只需要一个自己的域名(不用其实也可以)就可以通过云函数来托管这个授权用的文件来实现通用的授权服务。

下面我们来看一下如何去做这么一个简易的基础服务。

需求分析

首先我们知道配置网页授权域名的时候需要在公众号添加这个域名,要求我们在服务器上上传一个验证文件,并且这个文件要挂在根目录下才可以访问到,这就要求我们增加一个文件上传功能

这种情况下云函数就需要具备如下能力:

显然自己手动从零编写一个云函数就有些繁琐了,不过还有我们有内置应用模板帮助简化工作量。

实现步骤

应用创建

云函数后台直接创建应用,使用 koa 模板。

deploy-scf-koa.png

应用修改

应用创建好之后会在云函数列表里出现名为koa-starter函数,我们需要修改这个函数代码

应用模板的源码在 github 上就可以获取->koa-starter

这里讲解一下几个核心修改的实现吧:

  1. app.js增加文件上传支持,小文件是可以直接上传的。
app.use(
    koaBody({
        multipart: true,
        formidable: {
            multipart: true,
            maxFileSize: 400 * 1024 * 1024 // 设置上传文件大小最大限制,认4M
        }
    })
)
  1. 然后增加一些全局的处理
// 全局异常处理
app.use(async (ctx, next) => {
    try {
        await next()
    } catch (err) {
        ctx.body = {
            code: -1,
            data: ctx.data,
            message: ctx.msg || err.message || '服务开小差了,请稍后再试',
            etime: Date.Now()
        }
    }
})

// pretty json result
app.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()
        }
    }
})
  1. routes/index.js增加上传文件的路由处理
router.post("/uptxt", async (ctx, next) => {
    if (!ctx.request.files) {
        ctx.data = '未选择文件'
        await next()
        return
    }
    // 获取上传文件
    let file = ctx.request.files.file
    // 创建可读流
    let reader = fs.createReadStream(file.path)
    let filePath = `/tmp/${file.name}`

    // 创建可写流
    const upStream = fs.createWriteStream(filePath)
    await reader.pipe(upStream);
    ctx.data = file.name
    ctx.code = 0
    ctx.msg = '上传验证文件成功'
    await next()
});
  1. 前端的模板目录views下面增加两份页面代码

文件上传功能我们需要注意一点:

  • 云函数在执行过程中,都拥有一块 500MB 的临时磁盘空间 /tmp,用户可以在执行代码时对该空间进行一些读写操作,也可以创建子目录,但这部分数据在函数执行完成后不会保留。

因为需要上传一个验证文件所以这个临时目录自然会有这个 txt 文件,但是微信需要验证这个文件的有效性,所以这就意味着 tmp 目录下的东西需要被我们访问到,那该怎么办?

解决办法当然是有的,那就是手动修改静态资源目录为 tmp。

修改 app.js 文件

app.use(require('koa-static')('/tmp'))

这样在上传之后就可以直接访问到了。

  1. 首页上传页的路由处理:
router.get("/", async (ctx, next) => {
    await ctx.render("index");
});
router.get("/up", async (ctx, next) => {
  await ctx.render("up");
});
  1. 授权页的访问路由处理:
router.get("/auth.html", async (ctx, next) => {
  await ctx.render("auth");
});
  1. 保存代码并配置访问服务。

access-service.png

前端接入

vue 项目为例

插件引入:

在项目中加入生成回调地址的wechatAuth.js 文件

部分代码参考:

// 引入插件
import wechatAuth from '@/plugins/wechatAuth'

// 设置APPID
wechatAuth.setAppId(process.env.VUE_APP_WECHAT_APPID)

// 使用插件生成授权回调地址
wechatAuth.redirect_uri = processUrl()

/**
 * 处理url链接
 * @returns {string}
 */
function processUrl() {
  // 本地环境拿code
  if (process.env.NODE_ENV === 'development') {
    // 中间授权页地址
    return `${process.env.VUE_APP_WECHAT_AUTH_URL}?backUrl=${window.location.href}`
  }
  const url = window.location.href
  // 解决多次登录url添加重复的code与state问题
  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
}

环境变量配置:

#appid 可填入申请的测试公众号id或者其它准备好的ID
VUE_APP_WECHAT_APPID=''

#authUrl 网页授权中间页
VUE_APP_WECHAT_AUTH_URL='云函数http访问服务地址/auth.html'

整个授权服务的流程可概括为下图:

微信oauth2公用网页授权服务流程.png

因为我们只是把获取微信授权 code 的过程统一放到了云函数去处理,所以多个项目在调试时都可以使用同一个地址,减少了资源占用,不失为一个可用方案。

我们仅需要一个云函数就可以实现微信授权的本地调试以及几个项目几个公众号共用一个授权服务,免去独立域名、独立服务器的烦恼。

服务 Demo 演示

这里提供了一个云函数网页授权服务的 Demo 地址:

http://cloud.xuedingmiao.com/

public-wx-page-auth-demo.png

参考资料

相关文章

谷歌翻译不能用了怎么办?最近有很多用户发现谷歌浏览器翻译...
ios17有不少新功能,此次更新重点升级了电话和短信的功能,新...
什么是IP地址?IP地址有什么用? 很简单,IP是整个TCP/IP协议...
网上找到的动图下载到本地保存时格式却成了webp,想要发表情...
小米手机一开相机就死机怎么处理? 处理手机一开相机就死机的...
充电宝押金什么时候可以退?具体操作充电宝退押金的操作步骤...