前端实现绕过源服务器限制嵌入iframe并解决图片防盗链

背景

官网想在新闻那里通过接口同步公众号上发布的文章,实际上微信官方是提供了相关 API 的:通过 article_id 获取已发布文章,但是后端说他没法获取到 access_token 且这个接口也没法得到所有的文章,所以他就自己使用 python 爬取,但是最终文章内容,只能返回一个微信文章链接给我(一般不都直接是富文本嘛),所以我要实现的就是在网站上,根据一个链接,将其内容展示出来

解决方案和遇到问题

那我当即就想到了用 iframe 啊,于是有下面代码:

<iframe 
   src="https://mp.weixin.qq.com/s?xxxxxxxxxe#rd" 
   frameborder="0" 
   width="1000" 
   height="900"
/>

但是得到的结果却是:

在这里插入图片描述

查了查才知道,这是因为源服务器进行了某些设置,不允许被 iframe 嵌入,这就麻烦了,怎么搞呢?

解决iframe被源服务器阻止

我突然想起了我之前一个需求:

前端实现根据文件url批量打包为压缩包

iframe 中的请求也能被代理吗? 于是我决定也先本地配置代理试一试:

<iframe 
   src="/weixin-article/s?xxxxxxxxxe#rd" 
   frameborder="0" 
   width="1000" 
   height="900"
/>
// vue.config.js

module.exports = {
  // ...
  devServer: {
    // ...
    proxy: {
      '/weixin-article': {
        target: 'https://mp.weixin.qq.com/',
        secure: true,
        changeOrigin: true,
        pathRewrite: {
          '^/weixin-article': ''
        }
      }
    }
  }
}

结果发现真的可以:

在这里插入图片描述

但是图片不知道为什么一直处于加载中,检查元素并问了 chatgpt 后才发现,好家伙,src 属性搞了个不可见的元素,真正的图片路径存储在了 data-src 属性中:

在这里插入图片描述
在这里插入图片描述
这可如何是好?

解决iframe中图片始终处于加载中状态

要想解决就得想办法将 iframe 中的 img 标签的 src 属性用它的 data-src 属性替换,等于说要对 iframe 内部进行 DOM 操作,等于说是要在 iframe 元素内容都渲染之后再修改内部元素属性,这时候我突然想,既然 iframe 会自动通过 src 属性获取内容并渲染,那我可不可以主动请求 src 得到内容后(大概率是一串文档片段)修改内部 img 元素的 src 属性?关键就在于主动请求 iframesrc 后获取到的内容,是不是就是 iframe 渲染的内容

先主动请求iframe的src获取内容看看:

import axios from 'axios'

const realurl = '/weixin-article/s?xxxxxx#rd'
axios.get(realurl).then(res => {
  console.log('##################res:', res)
})

发现它返回的就是一个 html 文档:

在这里插入图片描述

那后续就容易了:

<iframe id="iFrame" src="" frameborder="0"  width="1000" :height="height" />
import axios from 'axios'

export default {
  data() {
    return {
      height: 0
    }
  },
  mounted() {
    const realurl = '/weixin-article/s?xxxxxxx#rd'
    axios.get(realurl).then(res => {
      if (res.data) {
        let html = res.data
        html = html.replace(/data-src/g, 'src') // 将 data-src 转化为 src
          .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '') // 移除HTML内容中所有的<script>标签,这样可以避免在iframe中执行潜在的不受信任的脚本。
          .replace(/https/g, 'http') // 将HTML内容中所有的https替换为http,可能是为了避免在HTTPS环境下加载非HTTPS资源导致浏览器警告
          .replace(/<html/g, '<html style="overflow: hidden;"') // 去掉滚动条
           // 还有一种方法去除滚动条,将 iframe 标签的 scrolling 属性设置为 no
        const html_src = html
        const iframe = document.getElementById('iFrame')
        iframe.src = html_src
        // 这时候就成功拿到了微信公众号文章的静态网页
        const doc = iframe.contentDocument || iframe.document
        // 将静态页面写入iframe中
        setTimeout(() => {
          // 获取文档高度赋值Iframe去除滚动条
          this.height = doc.documentElement.scrollHeight
        }, 500)
        // 发现 iframe 内容还是为空,找了半天发现这个元素被默认设置为不可见了,这里设置为可见
        doc.getElementById('js_content').style.visibility = 'visible'
      }
    })
  },
}

看一下效果:

在这里插入图片描述
这个。。。大概就是传说中的图片防盗链了吧?怎么解决呢?

解决图片防盗链

chatgpt 搜了搜:

在这里插入图片描述

好,那就试试,将 iframereferrer 设置为不发送,完善代码:

import axios from 'axios'

export default {
  data() {
    return {
      height: 0
    }
  },
  mounted() {
    const realurl = '/weixin-article/s?xxxxxxx#rd'
    axios.get(realurl).then(res => {
      if (res.data) {
        let html = res.data
        html = html.replace(/data-src/g, 'src') // 将 data-src 转化为 src
          .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '') // 移除HTML内容中所有的<script>标签,这样可以避免在iframe中执行潜在的不受信任的脚本。
          .replace(/https/g, 'http') // 将HTML内容中所有的https替换为http,可能是为了避免在HTTPS环境下加载非HTTPS资源导致浏览器警告
          .replace(/<html/g, '<html style="overflow: hidden;"') // 去掉滚动条
           // 还有一种方法去除滚动条,将 iframe 标签的 scrolling 属性设置为 no
        const html_src = html
        const iframe = document.getElementById('iFrame')
        iframe.src = html_src
        // 这时候就成功拿到了微信公众号,这个html_src是个静态网页
        const doc = iframe.contentDocument || iframe.document
        // 设置 iframe 中请求不发送 referrer,以绕过图片防盗链
        const htmlArr = html_src.split('</head>')
        const html_src_add = htmlArr[0] + '<meta name="referrer" content="never"></head>' + htmlArr[1]
        doc.write(html_src_add)
        // 将静态页面写入iframe中
        setTimeout(() => {
          this.height = doc.documentElement.scrollHeight
        }, 500)
        // 通过延时获取文档高度赋值Iframe去除滚动条,根据实际情况增加延时时间
        doc.getElementById('js_content').style.visibility = 'visible'
      }
    })
  },
}

ok!实现了:

在这里插入图片描述

线上代理配置

之前的代理只配置了本地开发时的代理,现在配置一下 nginx 实现线上代理,主要是在 nginx 的配置文件中添加以下代码:

server {
  #生产环境下获取微信公众号文章代理
  location /weixin-article/ {
      proxy_pass https://mp.weixin.qq.com/;
  }
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鹏北海-RemHusband

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值