V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
biguokang
V2EX  ›  程序员

一个关于 iframe 很艹蛋的前端需求

  •  
  •   biguokang ·
    AlpacaBi · 2021-02-02 15:49:46 +08:00 · 7393 次点击
    这是一个创建于 734 天前的主题,其中的信息可能已经有所发展或是发生改变。

    业务是这样的

    我们用 iframe 调用了 [被调用方] 的页面,然后 [被调用方] 页面一些 a 标签的 target 是“blank”的,这就导致点击他们的 a 标签会打开新的 tab

    而现在的需求是,在 iframe 里的任何操作,都 [ [不允许弹出新 tab 或者新窗口] ] ,如果 [被调用方] 的页面的确是要打开新 tab 的,那就改为在本地页面跳转。

    正常来说是无法实现的,因为跨域,而且 [被调用方] 肯定不会改代码的,所以只能写了个浏览器插件,通过注入代码的形式强行把网页里所有的 a 标签的 target 改为 self,这样 a 标签就全都是本地跳转

    但是 [被调用方] 网页里面的业务不一定全是用 a 标签来打开新 tab,他们可能一些业务是用 js 来打开新 tab 的(类似于 window.open 这种,我尝试在插件里把 window.open 覆写成 window.location.href,但也只是部分按钮实现了,有些还是会跳出新页面)

    现在问题如下: 1.被调用方是肯定不会改代码的,这个无解

    2.已经能通过写浏览器插件的方式来注入代码强改客户代码的 a 标签(因为被调用方是不同域的,而且非互联网项目,装插件的设备也就那么几台,也不麻烦)

    3.不能说这玩意不能做,因为项目跑在超高分大屏(电影院银幕大小)上,网页全屏显示,他有这种需求是因为打开新 tab 会强制关掉网页全屏,导致观感不好

    然后如何完美实现??(虽然以我的见解这玩意是无法实现的,用浏览器插件来搞已经很仁至义尽了)

    51 条回复    2021-02-04 05:59:33 +08:00
    SlipStupig
        1
    SlipStupig  
       2021-02-02 16:03:57 +08:00   ❤️ 3
    父页面往子页面做事件注入,监听点击事件并且 Hook 掉一些事件和函数,在父页面创建一个事件监听器,然后定义一个 receiveMessage 函数,子页面通过 window.postMessage 做跨域通信,父页面就能知道具体的点击事件了,新 tab 可以先请求页面然后覆盖掉原始的 body,这要不用跳转直接刷新了,思路仅供参考哈
    Osk
        2
    Osk  
       2021-02-02 16:04:05 +08:00 via Android
    chrome 的 --kiosk 模式试试?
    tanranran
        3
    tanranran  
       2021-02-02 16:09:51 +08:00
    写个浏览器的壳?
    cheng6563
        4
    cheng6563  
       2021-02-02 16:13:36 +08:00
    服务端也可以搞个[被调用方] 的代理程序,然后注入代码。可能比浏览器插件好用点?
    meepo3927
        5
    meepo3927  
       2021-02-02 16:14:52 +08:00
    用 Electron ? 没实践过,只是提供一个思路。

    或者 nginx 做代理,把 iframe 代理到 [被调用方] 的域上,这样 iframe 就是同域了,不知能否解决。
    yoshiyuki
        6
    yoshiyuki  
       2021-02-02 16:36:29 +08:00
    nginx 或者 nodejs 来代理被调用页面,使用 replace 把 target 替换掉
    biguokang
        7
    biguokang  
    OP
       2021-02-02 16:37:24 +08:00
    @SlipStupig 一开始和你想法一样,但是实际项目情况,注入不了,因为子页面是跨域的,contentDocoument 是 null 的,直接操作 contentWindow 直接报跨域错误,所以后来才想着写浏览器插件来注入代码才改的了子页面的逻辑。。。。。而且你想使用 postMessage 来通信,需要改被调用方的代码来接收的,但是实际场景被调用方的代码他们是不能改的。。。。如果可以改被调用方的业务代码,也不会有那么多事了
    zenxds
        8
    zenxds  
       2021-02-02 16:41:13 +08:00
    有些还是会跳出新页面?要具体看下有些是什么情况,比如 document.createElement 创建的 a 标签,需要把 document.createElement 也重写了
    mknightoy
        9
    mknightoy  
       2021-02-02 16:43:27 +08:00
    浏览器关掉跨域保护不就完了么,反正都是展示用的不需要跨域保护
    SlipStupig
        10
    SlipStupig  
       2021-02-02 16:56:31 +08:00
    @biguokang 关闭浏览器的跨域选项或者用 Nginx 做代理,都可以啊
    agee
        11
    agee  
       2021-02-02 17:15:30 +08:00
    自己做个套壳浏览器,就可以自己处理所有打开新窗口事件,这样不需要管网页上的代码了。理论上好像是这样,没具体试过。
    winterx
        12
    winterx  
       2021-02-02 17:19:22 +08:00   ❤️ 2
    难道就只有我想知道为什么被调用方不肯改代码?
    SakuraKuma
        13
    SakuraKuma  
       2021-02-02 17:21:48 +08:00
    支持 nginx 反代,把 target 都干掉
    除非额外有骚操作 js 跳转之类的
    biguokang
        14
    biguokang  
    OP
       2021-02-02 17:25:28 +08:00
    @winterx 我也想知道,但是人家就是那样,无解
    stillyu
        15
    stillyu  
       2021-02-02 18:09:34 +08:00
    设置浏览器禁止打开弹出窗口?
    DrakeXiang
        16
    DrakeXiang  
       2021-02-02 18:17:46 +08:00
    有浏览器扩展强制在同一个 tab 打开新页面
    SystemLight
        17
    SystemLight  
       2021-02-02 19:19:42 +08:00
    为什么不把 iframe 中内容读取出来,然后动态修改里面内容 再呈现呢 ?
    msmmbl
        18
    msmmbl  
       2021-02-02 20:18:11 +08:00 via Android
    可以用 nw.js 的话,他有一个 new-win-policy 和 navigation 事件可以控制跳转行为。
    leo108
        19
    leo108  
       2021-02-02 20:27:20 +08:00
    window.open = function(url) { location.href = url }
    window.open('https://v2ex.com')
    ciqulover
        20
    ciqulover  
       2021-02-02 20:35:21 +08:00 via iPhone
    既然都有浏览器插件了 咋还动不了 iframe 跳转逻辑呢?直接 intercept 掉 iframe 内的 script 加载返回体,正则全局替换 window.open,如果内联在 html 里的 script 代码,用 mutation observer 劫持替换就行。
    musi
        21
    musi  
       2021-02-02 23:40:26 +08:00 via iPhone   ❤️ 2
    其实 iframe 还有个 sandbox 属性可以设置 allow-popups

    详情左转 https://developer.mozilla.org/zh-cn/docs/web/html/element/iframe
    Hanada
        22
    Hanada  
       2021-02-03 00:01:15 +08:00
    为什么不考虑一下直接反向代理被调用方的网页,然后就可以根据需要进行改动了
    zqx
        23
    zqx  
       2021-02-03 06:51:08 +08:00 via Android
    避免 iframe 的安全策略,就肯定不能用 iframe 了。
    可以在你的 js 里,把别人的 html 请求回来,解析并插入到自己的 DOM 中,再请求 css js,是 vue 或 react 或原生 js,按对应的逻辑执行。关键难点在于要拿到他的路由和状态管理,如果他没有在代码里显式导出给你,就很难拿到,需要从源码里找
    微前端的实现大概也是这个思路
    sampeng
        24
    sampeng  
       2021-02-03 08:17:01 +08:00 via iPhone
    这你都干…怼回去啊。怎么不让做个屏幕随着衣服颜色变色呢?
    uiosun
        25
    uiosun  
       2021-02-03 08:26:07 +08:00
    iframe 不是可以禁止跳转吗……就在基本配置里啊……

    是我没理解对需求,还是你好歹去看看文档啊……
    zhw2590582
        26
    zhw2590582  
       2021-02-03 09:35:29 +08:00
    Chrome 可以监听新页面打开事件,看能不能在打开的时候马上关闭,并 iframe 重定向
    duduaba
        27
    duduaba  
       2021-02-03 09:51:25 +08:00
    iframe sandbox 属性,但是 ie9 前是不支持的。
    GM
        28
    GM  
       2021-02-03 10:24:43 +08:00
    自己写一个程序,内嵌 webview,不要用第三方浏览器,这样有 100%的控制权,随便搞。
    biguokang
        29
    biguokang  
    OP
       2021-02-03 10:26:01 +08:00
    @uiosun 老哥,我当然知道 iframe 是可以通过设置 sandbox 来禁止跳转,这个不用你教。。。问题是我的需求不是禁止跳转,而是把打开新窗口的逻辑改为在原地跳转
    biguokang
        30
    biguokang  
    OP
       2021-02-03 10:26:56 +08:00
    @coderfuns 这玩意我是有设置的,唯一的作用也就是禁止 iframe 页面打开新窗口而已,只不过满足不了需求,需求是把打开新窗口的操作变为原地跳转
    biguokang
        31
    biguokang  
    OP
       2021-02-03 10:29:00 +08:00
    @musi 这玩意我是有设置的,唯一的作用也就是禁止 iframe 页面打开新窗口而已,只不过满足不了需求,需求是把打开新窗口的操作变为原地跳转
    biguokang
        32
    biguokang  
    OP
       2021-02-03 10:30:52 +08:00
    @ciqulover 我是自己开发了一个浏览器插件,通过注入的方式解决了,说白了就是通过事件委托检测到点击了 a 标签后,然后禁止调 a 标签的行为,改为 window.location.href=xxx 就实现了。。。但只是解决了 a 标签的跳转逻辑,而 js 的跳转还没有
    shaoyijiong
        33
    shaoyijiong  
       2021-02-03 10:32:30 +08:00
    我们有一个类似的需求 我们是在后端写一个代理服务 , 前端请求那个代理 , 代理请求对应的网页 ; 然后在代理服务里面写入自己的 js 代码 , 就可以修改原来的 html 元素里面的 a 标签了 ; 或者直接修改原始 html 也行
    后端使用的是 java 做的代理 https://github.com/mitre/HTTP-Proxy-Servlet
    shaoyijiong
        34
    shaoyijiong  
       2021-02-03 10:40:51 +08:00
    也可以在嵌入的 js 中取消别人的点击事件
    no1xsyzy
        35
    no1xsyzy  
       2021-02-03 10:59:23 +08:00
    用 Nginx 反代改成同域啊
    Aria2 的老技巧了(
    dlllcs
        36
    dlllcs  
       2021-02-03 11:14:47 +08:00
    electron 可解
    ArthurSS
        37
    ArthurSS  
       2021-02-03 11:15:21 +08:00
    https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe
    sandbox
    该属性对呈现在 iframe 框架中的内容启用一些额外的限制条件。属性值可以为空字符串(这种情况下会启用所有限制),也可以是用空格分隔的一系列指定的字符串。有效的值有:
    ...
    allow-popups: 允许弹窗 (例如 window.open, target="_blank", showModalDialog)。如果没有使用该关键字,相应的功能将自动被禁用。
    ...
    mxT52CRuqR6o5
        38
    mxT52CRuqR6o5  
       2021-02-03 11:17:10 +08:00
    不能改代码,那能不能看代码,遇到没拦截住的就具体看看代码里是咋跳的,就知道为啥没拦截到了
    ArthurSS
        39
    ArthurSS  
       2021-02-03 11:19:07 +08:00
    @ArthurSS #37 刚刚没看到楼上评论。。。
    krapnik
        40
    krapnik  
       2021-02-03 11:55:25 +08:00
    19 楼的方法解决不了嘛?
    CODEWEA
        41
    CODEWEA  
       2021-02-03 12:56:08 +08:00
    我做过类似的,用 postMessage 通信即可
    lixiangzaizheli
        42
    lixiangzaizheli  
       2021-02-03 13:19:39 +08:00
    重写跳转注入不就好了
    ganbuliao
        43
    ganbuliao  
       2021-02-03 13:21:33 +08:00
    解决办法就是 : 劫持注入 js ,插件注入 js,套壳写浏览器,
    biguokang
        44
    biguokang  
    OP
       2021-02-03 13:52:22 +08:00
    @CODEWEA postMessage 无解,改不了被调用方的代码
    biguokang
        45
    biguokang  
    OP
       2021-02-03 13:53:32 +08:00
    @mxT52CRuqR6o5 被调用方的代码是 webpack 打包过的,变量名也被打包工具魔改了,基本无可读性
    PineappleBeers
        46
    PineappleBeers  
       2021-02-03 15:16:36 +08:00
    我觉得你当前的需求用 4 楼和 19 楼的方法结合就来就可以解决。
    浏览器请求你的服务。
    服务从被调用方的地址获取到页面。
    从获取到的 html 里注入一个 script 标签,标签里的内容就是重写 window.open 。
    然后将注入后的页面返回浏览器。
    biguokang
        47
    biguokang  
    OP
       2021-02-03 17:25:29 +08:00
    @PineappleBeers 他们可能没看完我的问题,我问题里说了已经我已经 override 了 window.open,但也只是一部分生效而已。。。而且所有的 a 标签已经被我的注入代码改成 target=_self 了
    zhuweiyou
        48
    zhuweiyou  
       2021-02-03 17:42:07 +08:00
    被调用方的页面 用服务端代理 + 改写
    R18
        49
    R18  
       2021-02-03 17:48:05 +08:00 via Android
    制作专用的浏览器呢?
    autoxbc
        50
    autoxbc  
       2021-02-04 04:29:09 +08:00
    并不需要读懂对方代码,只要把能够进行地址跳转的函数全部覆盖一遍,大概一只手就能数过来
    billccn
        51
    billccn  
       2021-02-04 05:59:33 +08:00
    想到我十年前做的老项目,当时是为一个触摸屏的工业设备做一些辅助功能,公司不想花钱去买厂家的 SDK,就要求利用内置的浏览器实现,最骚的就是偶尔要内嵌显示另一个网站,要可以交互,但是不能允许该网站弹窗或者影响宿主页面,这些是 iframe 做不到的(有这些功能那机子的内存也不够)。

    最后发现那个内置的浏览器可以支持 Java applets,就直接在网页里套了个 Java 版的 VNC 客户端,连接到服务器上一个无壳的 Chrome,弹窗什么的都只在服务器上进行,怎么也不会影响到客户端,而且客户端可以看到所有的效果。现在时代进步了,VNC 客户端可以直接用 JS 实现了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   实用小工具   ·   3240 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 46ms · UTC 12:02 · PVG 20:02 · LAX 04:02 · JFK 07:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.