Rain's Blog

微信公众号开发入门

Rain, Mon Jul 10 2023back

前言

如果当前的你并没有微信公众号,又想尝试有关微信网页技术,那也是可以的,因为微信提供了测试号,无需公众帐号,直接体验和测试公众平台所有高级接口,点击访问测试平台

访问上面的网址完成扫码授权后,我们会拿到一些测试号信息,分别是appIDappsecret,如下图

info

接下来就来到了开发中最关键的一步,如果这一步过不了接下来的工作都没法完成,官网是这样描述的。

请填写接口配置信息,此信息需要你有自己的服务器资源,填写的URL需要正确响应微信发送的Token验证

意思是说我们需要把代码上传到线上服务器才能响应微信的验证,但这样对我们来说并不友好。原因之一就是不能在 ide 实时地进行代码调试。办法总比困难多,只要配置下内网穿透就能轻易解决。在这里推荐使用的是 ngrok 。官网有教程且操作十分简单,这里便不过多赘述。相关网址:https://www.ngrok.cc

查看微信消息接口使用指南得知发送验证请求之前需要填写服务器配置,如下图

config

第一个输入框需要填写的是我们本地做验证微信服务器的接口地址,假设本地的接口地址是 /configVerification,开启本地服务,拿到内网地址以及端口号,通过ngrok映射过后的外网地址假设是 http:answer-ngrok.com,那么拼接起来即是:http:answer-ngrok.com/configVerification,而 token 则是自定义用来校验的。

代码层面,依次编写wxconfig,controller,service

新建 wxConfig 文件,将微信提供的信息以及自己定义的字段保存到这个文件

const wxConfig = {
  wxRequestPath: 'https://api.weixin.qq.com',
  wxAccount:'your account',
  wechatOpt: {
    appID: 'your appid',
    appsecret: 'your appsecret',
    token: 'your token',
    scoped: 'snsapi_userinfo',
    // 授权成功重定向地址,可以是前端地址也可以是后端接口地址
    // 如果重定向到后端接口地址,后端可以在当前service中做操作,比如使之回到前端的页面或第三方页面
    redirect_url: `your rediretUrl`,
  },
}
export default wxConfig

新建 controller 配置官网填写的用来验证接口地址,调用service

// controller
@Get('/configVerification')
@ApiOperation({ summary: '微信接口配置信息校验' })
wxConfigVerification(@Query() params: IwxVerification) {
  return this.wxService.wxVerificationService(params)
}

新建 service 根据官网示例代码进行编写

// service
async wxVerificationService(params: IwxVerification) {
  try {
    const { nonce, signature, timestamp, echostr } = params
    const { token } = wxConfig.wechatOpt
    const str = [token, timestamp, nonce].sort().join('')
    const sha1Str = sha1(str)
    if (sha1Str === signature) {
      return echostr
    } else {
      throw new errResult(201, '校验失败')
    }
  } catch (error) {
    throw new errResult(500, error)
  }
}

website

编写完成后进入测试环节,运行服务,回到微信官网,点击保存按钮,随后微信就会向你填写的地址发送一个请求,如果ngrok 用来映射的地址与你本地服务地址一致,那么此时不管验证成功或失败,你都可以拿到微信返回的校验参数,最后就是通过sha1加密库进行加密匹配。如果匹配成功网页就会弹窗成功,反之失败。

需要注意的是匹配成功是直接返回echostr信息,一般我们都是在框架中测试这一部分,有些框架会自带拦截器或是我们自己封装,把数据包装一遍再抛出去,所以我们要对这一步做白名单

获取微信用户的基本信息

  • 第一步,填写js接口安全域名

    website

    需要注意的点是这里它要的是一个域名而不是一个网址。所以不用带http前缀,也不用带地址后缀,把被 ngrok 映射过的域名填入即可

  • 第二步,找到网页账号的功能,网页授权获取用户基本信息,

    点击右侧修改,出现弹窗

    redirect

    这里需要的也是一个域名,起初我以为是一个重定向地址,其实不是,反正跟第一步填一样就行。

配置完成,开始编写代码层面

这里先理清下思绪,大致分为三步

第一步:用户同意授权,获取code

第二步:通过code换取网页授权access_token

第三步:拉取用户信息(需scope为 snsapi_userinfo)

  • 跳转微信公众号授权网页

    这一步可以在前端做或是在后端做,这里是交给后端。

    // controller
    @Get('/redirect')
    @ApiOperation({ summary: '微信授权定向跳转' })
    wxRedirect() {
        return this.wxService.wxAuthorizePathService()
    }
    

    可以看到 return 了一个微信地址,这个地址直接在官网文档复制然后把对应的参数配置成自己的,对应 wxConfig文件,着重是 redirect_url,这个是授权成功后的重定向地址,同可以设置成前端的地址还是后端的,在前面已经说过了。

    //service
    async wxAuthorizePathService() {
      return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${wxConfig.wechatOpt.appID}&redirect_uri=${wxConfig.wechatOpt.redirect_url}&response_type=code&scope=${wxConfig.wechatOpt.scoped}&state=STATE#wechat_redirect`
    }
    

    一切就绪,当前端发起请求后就会拿到这个地址进行跳转就会进入微信公众号授权页面,当用户同意授权之后,前面配置的 redirect_url 就会生效,从而在当前浏览器中跳转到这个redirect_url地址,前面说了这个可以是前端的页面地址也可以是后端的接口地址,而微信就会在这个地址后面拼接上一个code参数。有了这个code就可以拿到token,有了token就可以请求微信的接口拿到用户的基本信息。

  • 授权完成获取用户基本信息

    定义一个获取用户信息的路由

    // controller
    @Get('/userinfo')
    @ApiOperation({ summary: '获取授权后的用户信息' })
    wxGetAuthUserInfo(@Query('code') code: string) {
      return this.wxService.wxGetAuthorizationUserInfo(code)
    }
    

前面说过获取用户信息需要通过code换取token,有了token之后才能拉取用户信息,而这两步操作是需要请求微信服务器的,所以这里使用axios进行网络请求。

// 获取access_token
export const getAccessTokenApi = (code:number | string):Promise<IwxAccessToken | any> => {
  return request({
    url: `${wxConfig.wxRequestPath}/sns/oauth2/access_token?appid=${wxConfig.wechatOpt.appID}&secret=${wxConfig.wechatOpt.appsecret}&code=${code}&grant_type=authorization_code`,
    method: 'get',
  })
}
// 获取用户信息
// params参数均是获取token后微信返回的
export const getUserInfoApi = (params: IwxUserInfo): Promise<IwxGotItUserInfo | any> => {
  return request({
    url: `${wxConfig.wxRequestPath}/sns/userinfo?access_token=${params.access_token}&openid=${params.openid}&lang=`,
    method: 'get',
  })
}

最后来编写service层

async wxGetAuthorizationUserInfo(code: string | number) {
   try {
     const res: IwxAccessToken = await getAccessTokenApi(code)
     if (res.access_token) {
       const info: IwxGotItUserInfo = await getUserInfoApi(res)
       if (info.openid) {
         console.log('这是用户信息', info)
         return info
       }
     }
   } catch (error) {
     throw new errResult(500, error)
   }
 }