vue2-Django3-iframe解决方案,处理安全策略,事件拦截,处理iframe状态保持等

目录

简介

实现iframe

后端安全策略

通过Ngnix代理实现SAMEORIGIN

iframe的事件拦截,自定义处理

iframe的状态保持(解决vue中iframe重载)

解决方法


简介

Iframe(内联框架)是一种HTML元素,用于在网页上嵌入另一个HTML文档。然而,出于安全考虑,浏览器实施了同源策略,这意味着来自不同源的文档不能通过JavaScript互相交互。

实现iframe

<template>
    <div>
        <el-card class='container-card-second' v-loading='loading' element-loading-text='加载中...'>
            <iframe
                id='iframeId'
                ref='myIframe'
                @load='handleIframeLoad'
                class='iframe-container'
                :src='url'
                width='100%'
                frameborder='0'
                allowfullscreen
            ></iframe>
        </el-card>
    </div>
</template>

然后可以通过响应式完成url的切换

 mounted() {
        this.url = "http://localhost:8080";
    }

后端安全策略

在Django/settings.py中有相关iframe的安全设置

# 定义iframe允许跨域请求的域名列表
# X_FRAME_OPTIONS = 'ALLOW-FROM http://localhost:8080/ http://192.168.252.227:8081/'

# X_FRAME_OPTIONS = 'ALLOW-FROM http://192.168.252.227:8081'
X_FRAME_OPTIONS = 'SAMEORIGIN'

Django的X_FRAME_OPTIONS配置项用于设置HTTP响应头X-Frame-Options,这有助于防止点击劫持攻击。以下是X_FRAME_OPTIONS的作用和案例:

  1. 作用:X-Frame-Options是一个HTTP响应头,用来指示浏览器是否允许页面在<frame><iframe><embed><object>中被展示。通过设置这个头,网站可以确保自己的内容不被嵌入到其他网站中,从而避免点击劫持攻击。1

  2. 默认设置:Django默认启用了XFrameOptionsMiddleware中间件,该中间件会为每个响应设置X-Frame-OptionsDENY,意味着页面不能在任何框架中被展示。1

  3. 全局设置:可以通过在Django的设置文件中设置X_FRAME_OPTIONS变量来改变全局的X-Frame-Options值。例如,将其设置为SAMEORIGIN,允许页面在相同源的框架中被展示。

  4. 局部设置:通过ALLOW-FROM可设置自定义的网址,但是没有SAMEORIGIN好用。

ALLOW-FROMSAMEORIGINX-Frame-Options HTTP响应头的两个不同的指令,它们控制页面是否可以在<frame><iframe><embed><object>中被展示,但它们的应用范围和安全性有所不同:

  1. SAMEORIGIN:

    • SAMEORIGIN指令告诉浏览器只允许来自同一个源(即相同的协议、域名和端口)的页面在框架中展示当前页面。

    • 这个指令提供了一定程度的安全性,因为它限制了页面只能在相同源的上下文中被加载,从而避免了跨站点的点击劫持攻击。

    • 然而,如果网站有多个子域,并且需要在这些子域之间共享内容,SAMEORIGIN可能不够灵活。

  2. ALLOW-FROM(注意:并非所有浏览器都支持):

    • ALLOW-FROM指令允许指定页面可以在来自特定来源的框架中展示。这需要指定一个具体的URL。

    • 例如,如果设置为ALLOW-FROM https://example.com,那么只有来自https://example.com的页面才能在框架中展示当前页面。

    • 这个指令提供了更细粒度的控制,允许网站与特定的合作伙伴或服务共享内容,同时仍然保持较高的安全性。

区别:

  • SAMEORIGIN是一个更保守的策略,适用于大多数情况,因为它只允许相同源的页面加载。

  • ALLOW-FROM提供了更细粒度的控制,但需要谨慎使用,因为它可能会引入安全风险,特别是如果允许的来源不够安全或被恶意利用。

请注意,ALLOW-FROM并不是所有浏览器都支持,而且它的使用可能会更加复杂,因为需要正确地指定允许的来源。相比之下,SAMEORIGIN是一个更广泛支持且更安全的选项。在实际使用中,应根据具体需求和安全考虑来选择合适的指令。

通过Ngnix代理实现SAMEORIGIN

首先vue开发的前端一般都会使用ngnix代理,那么只需要新增一个在此端口下新增一个path用于代理你的iframe的内部页面。

例如

		server {
        listen       8081;
        server_name  localhost;
        location /api {
		        #需要代理访问的后端服务器地址
		         proxy_pass http://localhost:8000;
		        #重写以/api为baseURL的接口地址
		        rewrite "^/api/(.*)$" /$1 break;
		 }
        }

根据上述配置:

如果你将 iframe 内容通过 Nginx 代理设置在 8081/api,并且你的前端页面也是通过 8081 端口提供服务的,那么即使实际内容来自于后端服务(假设是 8000 端口),从浏览器的角度来看,这些请求都是发往同一个源(即 localhost:8081)。

在这种情况下,当浏览器对 iframe 请求执行同源策略检查时,它会认为所有请求都是从同一个源发起的,因为它们都有相同的协议(例如 http)、域名(例如 localhost)和端口号(8081)。所以,在这种配置下,就算实际上 iframe 的内容是由不同端口的后端服务生成的,由于浏览器只能"看到" 8081 端口,X-Frame-Options 设为 SAMEORIGIN 是可以工作的,因为它满足了从同一来源加载 iframe 内容的要求

Nginx首先将请求转发到http://localhost:8000

接着,rewrite指令将请求的URI从/api/users重写为/users

最终,后端服务将收到不带有/api前缀的请求路径,即/users

那么你就可以通过http://你的项目ip:8081/api 来访问你的iframe网页

至此若你的iframe是内部网页则可以通过此方案实现SAMEORIGIN

iframe的事件拦截,自定义处理

使用@load='handleIframeLoad'和ref='myIframe'

addEvent(iframe) {
            iframe.contentWindow.document.addEventListener('click', event => {
                // 获取超链接的href属性
                const href = event.target.href;
                // 定义你希望拦截并使用Vue路由处理的URL部分
                const mark_val = '/xxx_edit';
                // 如果href包含mark_val,则进行Vue路由导航
                if (href.includes(mark_val)) {
                    // 阻止默认行为
                    event.preventDefault();
                    // 提取ID或其他必要的路由参数
                    const id = href.split('/').pop();
                    // 使用Vue路由进行导航
                    this.$router.push(`${mark_val}/${id}`);
                }
                // 若不符合特定条件,则不阻止默认行为,链接将按原本方式跳转
            });
        },
        handleIframeLoad() {
            const iframe = this.$refs.myIframe;
            if (iframe) {
                this.addEvent(iframe);
            }
            setTimeout(() => {
                this.loading = false;
            }, 1500);
            setTimeout(() => {
                this.addEvent(iframe);
            }, 3000);
        }

笔者增加延时是为了防止页面加载缓慢,没有成功绑定到事件。这里演示了拦截点击事件然后做处理。

iframe的状态保持(解决vue中iframe重载)

上述步骤都完成后,你可能会发现你的页面在子页面切换的时候总是会重载iframe的url。

1.Vue 的缓存机制并不是直接存储 DOM 结构,而是将 DOM 节点抽象成了一个个 VNode节点。因此,Vue 的 keep-alive 缓存也是基于 VNode节点 而不是直接存储 DOM 节点。在需要渲染的时候从Vnode渲染到真实DOM上。

2.iframe中keep-alive机制失效原因:iframe页里的内容并不属于节点的信息,所以使用keep-alive依然会重新渲染iframe内的内容。而且iframe每一次渲染就相当于打开一个新的网页窗口,即使把节点保存下来,在渲染时iframe页还是刷新的

解决方法

找到项目的路由入口,然后针对iframe页面做非keep-alive的处理。

以下为实例代码

            <div class='content'>
                <transition name='move' mode='out-in'>
                    <!--                        isRouterAlive && !$route.meta.iframe 排除iframe页面的显示由下面的独立的iframe页面实现显示-->
                    <keep-alive :include='tagsList' :max='15'>
                        <router-view
                            :key='$route.fullPath'
                            ref='routerView'
                            v-if='isRouterAlive && !$route.meta.iframe'
                        />
                    </keep-alive>
                </transition>
                <!--                独立的iframe页面;根据路由的fullPath(包括路由的参数 方便处理不同参数的同一个组件)判断是否显示-->
                <component class='no-iframe-page'
                           v-for='item in hasOpenComponentsArr'
                           :key='item.fullPath'
                           :is='item.name'
                           v-show='$route.fullPath === item.path'
                ></component>
            </div>

其中逻辑部分为

    computed: {
        comTags() {
            return this.$store.state.globalTagsList;
        },
        /**
         * 利用全局响应式的标签列表过滤iframe页面,并且可以实现动态打开iframe页面
         * @returns
         */
        hasOpenComponentsArr() {
            return this.comTags.filter(item => item.meta && item.meta.iframe);
        }
    }

globalTagsList是利用vuex(此处的使用方式参考点我查看)将标签页做了全局状态管理。

例如

        tagsList: {
            get() {
                return this.$store.state.globalTagsList;
            },
            set(value) {
                this.$store.commit('SET_GLOBAL_TAGS_LIST', value);
            }
        }

笔者通过路由的元信息来实现iframe页面的区分,以下为路由部分的代码实例

                {
                    path: '/xxx/:id',
                    component: resolve => require(['../views/xxx/xxx.vue'], resolve),
                    meta: { title: 'xxx', iframe: true },
                    name: 'xxx'
                }]

至此iframe页面可以完全作为整个web页面内的子页面来使用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Franciz小测测

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

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

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

打赏作者

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

抵扣说明:

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

余额充值