使用iframe将帆软报表集成在Vue大屏中,过程中遇到好几个坑,特此记录。

1.思路

  1. 请求帆软获取token的接口拿到token,存到cookie中

  2. 携带cookie请求帆软的报表页面。将其以iframe的形式嵌入vue报表中

2.实现

2.1 请求获取token接口

·跨域问题

这里遇到了第一个问题,请求接口的时候 http get请求报错403,浏览器报错

Access to XMLHttpRequest at
'http://帆软服务器ip/getToken' from origin 'http://代理服务器ip' 
has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

是浏览器的同源策略引起的跨域问题。
经过查阅资料,生产环境解决跨域问题的办法有很多JSONP,CORS,反向代理等。
这里我采用的是反向代理。
只需要在代理服务器那里做个反向代理即可。项目部署架构如下:
图片1
一开始我以为使用的Nginx做的代理,同事反馈说是使用的Apache做的代理,两段设置都贴上吧:

Nginx:(没测试过)
location ^~ /getToken{
  proxy_pass http://帆软服务器ip/getToken;
}

Apache:
ProxyRequests Off
  ProxyPass /getToken  http://帆软服务器ip/getToken
  ProxyPassReverse /getToken  http://帆软服务器ip/getToken

注意: 这里我犯了个低级错误。设置了代理以后,那么发Axios请求的时候,就应该是请求服务器的ip,而不是直接请求帆软的ip,即:

utils.ajax.get("http://帆软服务器ip/getToken", {}) // 错误
utils.ajax.get("http://代理服务器ip/getToken", {}) // 正确

2.2 将token存到cookie中

这里无需多说

setCookie(cname, cvalue, exhours) {
  let d = new Date();
  d.setTime(d.getTime() + (exhours * 60 * 60 * 1000));
  let expires = "expires=" + d.toUTCString();
  document.cookie = cname + "=" + cvalue + "; " + expires;
},
getCookie(cname) {
  let name = cname + "=";
  let ca = document.cookie.split(';');
  for (let i = 0; i < ca.length; i++) {
    let c = ca[i].trim();
    if (c.indexOf(name) === 0)
      return c.substring(name.length, c.length);
  }
  return null;
},
removeCookie(cname) {
  this.setCookie(cname, '', -1);
}

2.3 携带token请求帆软报表页面

· cookie跨域问题

首先仍然是跨域问题,当iframe中的地址跟地址栏中的地址存在跨域时,请求将不能携带cookie。具体表现为:即使我拿到了token存在cookie中,请求报表页面时还会被拦截到登陆注册页,而且输入账号密码也无法登陆,无限循环登陆注册页。
解决仍然采用Apache的反向代理:

ProxyPass /fineBI/decision http://帆软服务器ip:port/fineBI/decision

· iframe 额外携带头文件

解决了跨域问题之后,虽然iframe的src携带了cookie,但仍然无法登陆,经过跟帆软的沟通,他们还需要额外在请求头提供Authorization: Bearer token来进行身份校验(我nm…)
iframe src是无法携带请求头的,只能另辟蹊径了。

这里参考了这位大大的文章: VUE+iframe添加请求头

<iframe id="iframe" src="" />

setTimeout(() => {
  var iframe = document.querySelector("#iframe");
  this.populateIframe(iframe, [["Authorization", "Bearer " + getToken()]]);
}, 0);

methods: {
   populateIframe(iframe, headers) {
     var xhr = new XMLHttpRequest();
     xhr.open("GET", 'http:localhost:8080/xxx');
     xhr.responseType = "blob";
     headers.forEach((header) => {
       xhr.setRequestHeader(header[0], header[1]);
     });
     xhr.onreadystatechange = () => {
       if (xhr.readyState === xhr.DONE) {
         if (xhr.status === 200) {
           iframe.src = URL.createObjectURL(xhr.response);
         }
       }
     };
     xhr.send();
   },
 }

· XMLHttpRequest中responseType 采用blob的坑

这样改写完成以后,确实可以请求网址得到帆软的页面元素,但是仍然有两个问题:

  1. 帆软iframe中script中的jquery语法会报错。(注:如果不镶嵌,直接用浏览器打开则不会有这个问题)
    tupian 2
  2. 获得的页面元素元素中,body是空的。
    在这里插入图片描述虽然没弄明白为什么为这样,有知道的朋友可以留言告诉我。但是页面显示不出来是肯定不行的。期间也尝试过给整个vue项目装jquery,但是引入的第三方页面并不能共享项目中的jquery。

经过帆软前辈的提醒,考虑到是不是xhr.responseType = "blob"这一行的问题,遂进行修改,使用 xhr.responseType = "text"

修改后的方法:

populateIframe(iframe, headers) {
     var xhr = new XMLHttpRequest();
     xhr.open("GET", 'http:localhost:8080/xxx');
     xhr.responseType = "text";
     headers.forEach((header) => {
       xhr.setRequestHeader(header[0], header[1]);
     });
     xhr.onreadystatechange = () => {
       if (xhr.readyState === xhr.DONE) {
         if (xhr.status === 200) {
           // iframe.src = URL.createObjectURL(xhr.response);
           let iframe = document.getElementById('frame-box');
            iframe = iframe.contentWindow || ( iframe.contentDocument.document || iframe.contentDocument);
            iframe.document.open();
            iframe.document.write(xhr.response);
            iframe.document.close();
         }
       }
     };
     xhr.send();
   },

修改之后成功把页面展示了出来。

· 页面能显示不能导出。cookie path的问题

就在我十分欣喜准备写篇博客庆祝一下的时候,噩耗又来了。页面是可以展示了,但是不能使用报表页面上的导出和打印功能,我他喵的。得,继续看吧。
在这里插入图片描述

根据我英语六级的水平判断,报表在打印之前,会发一个请求来检查是否登陆。
图片5
肯定是这个请求导致它判断我没有登陆。我又让同事分别在我们嵌入的报表页面和在浏览器上的报表页面点击导出来判断头文件还存在哪些不同,经过对比,我发现
我从帆软接口请求到的token, 只附带到了header Authorization中,并没有附带到cookie中,而我本地测试的时候确实是附带到了cookie中。这令我百思不得其解。
经过详细的对比,把问题锁定在cookie的path上:
本地:
在这里插入图片描述
服务器:
在这里插入图片描述
经过打包,服务器上的path,变为了/myprojectname。cookie中的path是cookie生效的范围。path不同,自然没法附带这个cookie。
所以我们修改上面提到的getCookie函数:

document.cookie = cname + "=" + cvalue + "; " + expires + '; path=/;'

至此,打印、导出也可以正常使用。
梳理一下认证过程,之前页面单点登录的时候,cookie未附带也登录成功,说明登录的时候只验证了Authorization,而打印、导出的时候又只验证cookie,不得不感叹帆软的验证方式有点儿奇怪。


左手是过目不忘的萤火,右手是十年一个漫长的打坐。

Logo

前往低代码交流专区

更多推荐