主动劫持DNS助力前后端分环境开发微信网页项目

有了前两篇关于微信网页项目与前后端分环境开发的思考,帅华君将在本篇介绍通过修改本地DNS解析来绕过微信服务器的验证,并通过Nodejs搭建本地服务作为中间件转发API请求到远程服务器,获得用于配置JS-SDK的一系列参数,从而正常调用JS-SDK提供的丰富API。

准备工作

两个域名

以帅华君的个站为例, 帅华君使用 shuaihua.cc 二级域名用来作为微信的安全域名,模拟真实项目中普通用户访问的域名,需要在微信公众平台后台或微信测试账号相应位置设置此域名。

www.shuaihua.cc 三级域名用来作为提供远程API接口的域名,模拟真实项目中专门用于提供接口的域名,普通用户基本不会访问,当然,既然该三级域名是用来提供API的,所以作为API使用更语义化的三级域名命名应该类似于Github的 api.github.com。不过这两个域名用来模拟真实的情况没有问题。

修改DNS

mac系统hosts文件所在目录为 /private/etc/hosts,将该文件拖拽直桌面,使用任意文本编辑器打开,根据上方的规定,将 shuaihua.cc 二级域名劫持到 127.0.0.1,保存后将文件拖拽回原位置即可:

如此,启动本地Nodejs搭建的本地服务,当在浏览器中输入 shuaihua.cc 便可访问到本地服务器,也就成功把帅华君个站真正的DNS解析服务器劫持在摇篮里了。有害或者无害并不是绝对的,这是我想要的,所以是无害,而病毒恶意修改hosts则是有害,有害或无害要看是主动或是被动。

写着写着就领悟到了关于一切对立面的真实含义:就像蛋壳从里面打破是新生,是有益,从外面打破是毁灭,是有害!人亦如此。

微信网页授权

获取CODE

上一篇文章介绍了如何通过微信授权获取用户信息,请求地址如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

上面帅华君已经将 shuaihua.cc 设置为JS接口和OAuth2.0网页授权回调页面安全域名,因此请求参数中的 redirect_uri 设置为URI以 http://shuaihua.cc/ 起头的回调路径即可。

以微信测试公众号为例,绑定开发者后,在开发者工具内访问如下地址:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxa0bb1e90533c6981&redirect_uri=http%3A%2F%2Fshuaihua.cc%2F&response_type=code&scope=snsapi_base&state=123#wechat_redirect

微信验证成功后自动将页面重定向到如下地址:

前端获取到code值之后,向后台发起Ajax请求,后端使用APPID/SECRET/CODE三个参数获取到用户与公众号产生的唯一OPENID,获取到OPENID,随后使用ACCESS_TOKEN和OPENID获得微信用户基本信息,再接下来就是项目的具体业务了。

获取OPENID

帅华君快速写了一个获取url查询参数的函数,当然真是项目中尽量不要重复造轮子。

const getQuery = () => {
  let search = window.location.search.replace(/\?/g,'')
  if(!search) return
  let query = search.split('&')
  let res = {}
  for(let i=0; i<query.length; i++){
    let _q = query[i].split('=')
    res[_q[0]] = _q[1]
  }
  return res
}

向提供API的三级域名 www.shuaihua.cc 发送请求获取openid,需要传入code值:

let QUERY = getQuery()
if(QUERY){
  if(QUERY.code){
    cco.ajax({
      url: `/api/getwepage_test_openid/?code=${QUERY.code}`,
      method: 'GET',
      onReceived: client => {
        console.log(client)
      }
    })
  }
}

远程服务器返回如下信息,成功获取用户的OPENID:

用户基本信息

由于CODE只能使用一次,因此相同的URI下,该接口不能重复调用,除非重新将用户引导至微信网页授权页面,获取新的CODE值。

获取到OPENID之后便可以如法炮制发送Ajax请求获取用户信息,后面就涉及到具体项目的具体业务,大同小异,不作赘述。

配置JS-SDK

有了上面的基础,接下来配置微信JS-SDK就顺水推舟了。

上面提到已经在微信测试账号后台将 shuaihua.cc 设置为了JS接口安全域名,因此直接,调后端写好的接口即可,唯一的参数是当前的URI(不包含锚标记)。

cco.ajax({
  url: `/api/getwepage_test_jsconfig/?url=${encodeURIComponent('http://shuaihua.cc/')}`,
  method: 'GET',
  onReceived: client => {
    let res = client.response
    console.log(res)
    wx.config({
      debug: true,
      appId: res.appid,
      timestamp: res.timestamp,
      nonceStr: res.noncestr,
      signature: res.signature,
      jsApiList: [
        'updateAppMessageShareData','updateTimelineShareData',
        'onMenuShareWeibo','onMenuShareQZone','startRecord',
        'stopRecord','onVoiceRecordEnd','playVoice','pauseVoice',
        'stopVoice','onVoicePlayEnd','uploadVoice','downloadVoice',
        'chooseImage','previewImage','uploadImage','downloadImage',
        'translateVoice','getNetworkType','openLocation','getLocation',
        'hideOptionMenu','showOptionMenu','hideMenuItems','showMenuItems',				 
        'hideAllNonBaseMenuItem','showAllNonBaseMenuItem','closeWindow',
        'scanQRCode','chooseWXPay','openProductSpecificView','addCard',
        'chooseCard','openCard'
      ]
    })
  }
})

为了方便,帅华君把所有的API全部包含进来了,调用上方示例代码中的 wx.config({...}) 后,程序也会向微信服务器验证,只有微信为服务返回的全部参数与我们的全部参数完全一致,才能使用JS-SDK提供的丰富接口。

微信服务器验证成功后会执行向 wx.ready() 传递的回调函数,验证是否验证成功,有成功就有失败,wx.error() 用来当验证失败是执行传入的回调函数。直接抛出以下代码:

wx.ready(function () {
  wx.chooseImage({
    count: 1,
    sizeType: ['original', 'compressed'],
    sourceType: ['album', 'camera'],
    success: function (res) {
      var localIds = res.localIds;
    }
  });
})

上方代码示例,使用JS-SDK选择图片的接口,在微信开发者工具中,直接弹窗提示该接口他调用成功返回的对象信息。

本文未涉及如何在本地部署需要支持HTTPs协议的方案,只有留个裂缝,阳光才能透进来,抛砖引玉,各抒己见吧。


2019年1月2日更新:

文章中没有介绍前端如何在本地开启HTTPs服务。

目前在做的项目中,线上采用HTTPs协议,因此前端在本地搭建的服务器也需要支持HTTPs协议,所以在这次更新里补充说明一下。

  • 利用openssl生成证书
    #生成私钥key文件
    openssl genrsa 1024 > /path/to/private.pem
    
    #通过私钥文件生成CSR证书签名
    openssl req -new -key /path/to/private.pem -out csr.pem
    
    #通过私钥文件和CSR证书签名生成证书文件
    openssl x509 -req -days 365 -in csr.pem -signkey /path/to/private.pem -out /path/to/file.crt
    
  • 利用Nodejs的https模块启动监听443端口号的本地服务
    const app = require('express')()
    const https = require('https')
    const options = {
      key: fs.readFileSync('./private.pem'),
      cert: fs.readFileSync('./file.crt')
    }
    const server = https.createServer(options, app).listen(443)
    

如此,配合文章中介绍的将 localhost 劫持为最终上线的域名(例如线上服务器的域名为 www.example.com

使用浏览器访问 https://www.example.com 即可访问到本地Nodejs搭建的服务。

下一篇《基于Nodejs+Canvas+WebSocket实现视频流直播H5应用》

上一篇《服务器端采用Nodejs实现微信网页授权及JS-SDK接口配置》

快速跳转 心头好文 - language - 《主动劫持DNS助力前后端分环境开发微信网页项目》

发布日期 2018-12-25 21:40:23 周二

版权声明 自由转载-非商用-非衍生-保持署名(创意共享3.0许可证