【补充】Django框架之IFrame中的跨域问题

发布时间 2023-07-21 18:59:52作者: Chimengmeng

【一】iframe 中的跨域问题详解

  • 在网页开发中
    • 浏览器会应用跨域安全策略,限制不同域名之间的交互。
    • 跨域问题指的是如果一个网页的脚本尝试访问另一个域名下的资源或与其通信时,浏览器会拒绝这样的请求。
  • 其中,使用 <iframe> 标签嵌套其他网页是一种常见的前端技术。
  • 然而,由于 <iframe> 中的内容与包含它的页面处于不同域名下,可能会导致跨域问题。
  • 下面我将详细解释 iframe 中的跨域问题。

【1】跨域问题的原因

  • 跨域问题主要是由于浏览器的同源策略所引起的。
  • 同源策略是浏览器的一项安全策略,它要求脚本只能访问同一域名、端口和协议下的资源。
  • 如果脚本尝试访问不同域名下的资源,浏览器会阻止这个跨域请求。

【2】iframe 中的跨域问题

  • 当在一个域名的页面中使用 <iframe> 标签加载另一个域名下的页面时,由于域名不同,就涉及到跨域问题。

  • 在这种情况下,以下操作将受到限制或无法执行:

    • 跨域通信:

      • iframe 中的页面无法直接通过 JavaScript 与包含它的页面进行跨域通信
      • 无法访问对方页面的 DOM 对象。
    • 跨域脚本操作:

      • iframe 中的页面不允许通过脚本来操作包含它的页面或其他不同域名下的页面。
  • 如果你尝试在 iframe 中的页面中使用 JavaScript 直接访问父窗口或其他域名下的页面,浏览器会报错并拒绝这个跨域请求。

【3】解决跨域问题的方法

  • 使用后端代理:

    • 通过在服务器端设置代理,将跨域请求发送给服务器,然后由服务器代为请求对应的资源,并将结果返回给前端页面。
    • 这样,前端页面就可以绕过跨域限制。
  • 跨文档通信(PostMessage):

    • 使用 window.postMessage 方法,通过向 iframe 发送消息,并在 iframe 中监听消息来实现跨域通信。
  • 跨域资源共享(CORS):

    • 服务器端设置相应的响应头部信息,允许特定域名的请求访问资源。
    • 这需要在服务器端进行配置,具体操作可参考 CORS 的相关文档。
  • 动态创建 <script> 标签:

    • 通过动态创建 <script> 标签,将不同域名下的脚本资源加载到当前页面中。
    • 这种方式被称为 JSONP,但需要后端配合支持。

【二】Django中的iframe 中的跨域问题

【1】引入

  • 在Django3.x版本以上有一个中间件就在帮我们在做这件事
  • 那就是
django.middleware.clickjacking.XFrameOptionsMiddleware

【2】介绍

  • django.middleware.clickjacking.XFrameOptionsMiddleware是Django框架中的一个中间件(middleware)

    • 用于提供防止点击劫持(clickjacking)攻击的功能。
  • 点击劫持是一种常见的网络攻击方式

    • 攻击者会在一个网页上放置透明的IFrame,并将目标网页叠加在其中,诱使用户误点击。
    • 为了防止这种攻击,Web浏览器提供了X-Frame-Options HTTP头部来告知浏览器是否允许网页在IFrame中展示。
    • XFrameOptionsMiddleware就是为了设置这个HTTP头部而存在。

【3】功能

  • 设置X-Frame-Options头部:

    • 当请求到达Django应用时,XFrameOptionsMiddleware会检查视图函数的返回结果,如果设置了X_FRAME_OPTIONS属性,中间件会将其值设置为X-Frame-Options头部的值。
    • 常见的取值有
      • "deny"(拒绝网页在任何IFrame中显示)
      • "sameorigin"(允许网页在相同域名下的IFrame中显示)。
  • 提供方便的接口:

    • 除了通过设置X_FRAME_OPTIONS属性来使用XFrameOptionsMiddleware
    • 还可以通过在settings.py文件中设置X_FRAME_OPTIONS全局变量来实现默认设置
    • 也可以通过在视图函数中使用@xframe_options_exempt装饰器来取消对某个视图函数的保护。

【4】示例

  • 在settings.py文件中进行全局设置:

    X_FRAME_OPTIONS = 'deny'
    
    X_FRAME_OPTIONS参数用于设置网页的嵌入方式,它的值可以是以下三种:
    
    1. 'DENY':
    	此设置将完全禁止页面嵌入到任何<frame>、<iframe>或<object>元素中。
    	这意味着如果其他网站试图将您的页面嵌入到它们的框架中,浏览器将拒绝加载您的页面。
    
    2. 'SAMEORIGIN':
    	此设置允许页面只在与其来源相同的<frame>或<iframe>中显示。
    	如果其他网站想要嵌入您的页面,但不是从同一个源(origin)加载的,浏览器将拒绝加载您的页面。
    
    3. 'ALLOW-FROM uri':
    	此设置允许将页面嵌入到指定的URI(统一资源标识符)指定的框架中。
    	您需要将"uri"替换为允许加载您页面的来源URI。
    	注意,该选项在现代浏览器中已被废弃,因为其安全性较低。
    
    目的是通过X-Frame-Options头部来控制页面是否可以作为<frame>或<iframe>的内容在其他页面中嵌入。
    
    例如,设置X_FRAME_OPTIONS = 'deny',当其他网站尝试将您的网页嵌入到框架中时,浏览器将拒绝加载您的网页。
    
  • 在views.py文件中添加需要保护的视图函数:

    from django.views.decorators.clickjacking import xframe_options_exempt
    from django.http import HttpResponse
    
    @xframe_options_exempt
    def unprotected_view(request):
        return HttpResponse("This view can be displayed in an iframe.")
    
    def protected_view(request):
        return HttpResponse("This view cannot be displayed in an iframe.")
    
    • 在以上代码中,unprotected_view函数通过使用@xframe_options_exempt装饰器来取消对其的保护。
  • 通过以上设置

    • 当用户访问protected_view时,浏览器会拒绝将该网页在IFrame中显示;
    • 而当用户访问unprotected_view时,浏览器会允许该网页在IFrame中正常显示。

【三】小结

  • django.middleware.clickjacking.XFrameOptionsMiddleware是一个轻量级的中间件

    • 旨在提供简单的防止点击劫持攻击的功能
  • 该中间件的主要责任是检查视图函数的返回结果,并根据需要设置X-Frame-Options头部的值。

  • 您可以通过

    • 在视图函数上设置X_FRAME_OPTIONS属性来自定义X-Frame-Options头部的值

    • 或者使用全局变量X_FRAME_OPTIONS在settings.py文件中进行默认设置。

    • 也可以使用@xframe_options_exempt装饰器来取消对特定视图函数的保护。

    • 也可以暴力的直接将中间件注释掉解决这个问题

【四】具体的案例

  • 在写BBS项目中,使用form表单向后端提交数据的时候,会用到一个 textarea 标签
  • 在这个标签中会用到富文本编辑器
  • 这个富文本编辑器会有一个功能,上传图片到后端,再返回给前端
    • Django3.x 以下没什么大问题
    • 但是Djang3.x以上就会在控制台抛出一个错误
Refused to display 'http://127.0.0.1:8000/' in a frame because it set 'X-Frame-Options' to 'deny'.
  • 解决方案如以上

    • 方案一:给相应的视图函数加装饰器
    from django.views.decorators.clickjacking import xframe_options_exempt
    @xframe_options_exempt
    def index():
        pass
    
    • 方案二:全局设置iframe头
    X_FRAME_OPTIONS = 'deny' 			# 拒绝网页在任何IFrame中显示
    X_FRAME_OPTIONS = 'SAMEORIGIN'		# 允许网页在相同域名下的IFrame中显示