美文网首页
跨域问题

跨域问题

作者: 鬼泣_89b6 | 来源:发表于2019-06-29 16:50 被阅读0次

什么是跨域

跨域是指一个域下的文档(脚本)视图去访问另一个域下的资源,这里的跨域是广义的

广义的跨域:
资源转跳:a连接、重定向、表单提交
资源嵌入:link标签 script标签 frame标签(html5不支持),还有样式中 background:url()、@font-face()等文件外链
脚本请求:JS发起的ajax请求,dom和JS对象的跨域操作等
狭义的跨域:
所谓狭义的跨域是由浏览器同源策略限制的一类请求场景

什么是同源策略

同源策略/SOP(Same origin policy) 是一种约定,由Netscape公司1995年引入浏览器,他是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 XSS CSRF等攻击.所谓同源指的是"协议 + 域名 + 端口" 三者相同,即便两个不同的域名指向同一个ip地址,也非同源

url的组成

关于xxs攻击/csrf攻击
https://www.cnblogs.com/wzj4858/p/8259944.html
https://baijiahao.baidu.com/s?id=1618267672561552800&wfr=spider&for=pc

同源策略限制了以下几种行为
1、cookie、LocalStorage(局部处理器,通过 JS 操作) 和 IndexDB(本地存储) 无法读取
2、DOM(HTML节点树) 和 JS 对象无法获得
3、AJAX 请求发送后,被浏览器拦截
但是有三个标签是允许跨域加载资源:
<img src='xxx'>
<link href='xxx'>
<script src='xxx'>

常见跨域场景
'当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。'

URL                                      说明                    是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不同文件或路径           允许
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不同端口                不允许
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名,不同协议                不允许
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名对应相同ip              不允许
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不同                不允许
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不同域名                         不允许

由此可得 当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。都会被浏览器拦截

特别说明两点:
第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。
第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”。

这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。

常见解决跨域问题的方案

1.通过jsonp跨域
2.跨域资源共享(CORS)
3.nginx反向代理

一 通过jsonp跨域

通常为了减轻web服务器的负载,我们把js css img 等静态资源分离到另一台独立域名的服务器上,在HTML页面中通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信

jsonp缺点:
有局限性,只能实现get一种请求;
不安全,可能会遭受xss攻击

jsonp和ajax对比:

jsonp和ajax相同,都是客户端向服务端发送请求,从服务器获取数据的方式.但是ajax属于同源策略,jsonp属于非同源策略(跨域请求)

  1. 原生实现:
下面有两个端口 8080 和 8088 因为端口的不同,导致非同源,所以,一旦进行访问,就会被拦截
在前后端不分离的情况下:
访问  path('index/',onBack)  index路由执行 onBack函数
函数:
def onBack(request):
    print('成功')
    return HttpResponse('少时诵诗书')
前端:
<script>
    // 创建 script 标签 并指定类型
    var script = document.createElement('script');
    script.type = 'text/html';

    // 指定路由 和 回调函数 callback=jsonCallback
    script.src = 'http://127.0.0.1:8088/hahaha?callback=jsonCallback';
    // 添加到 文件头中
    document.body.appendChild(script);

    // 回调函数
    function jsonCallback(res) {
        console.log(data);
    }
</script>
注意:前端转跳的是域名 'hahaha' 端口 8088 并且执行了回调函数 jsonCallback()

前端页面成功的显示了 少时诵诗书 这个关键词
单纯的拿到数据并不能让我们满意。一个合适的请求函数,必然包含对成功、失败、超时的处理,就像我们上面写的那个简单示例,一旦出现异常,就不能让我们满意了。
在这一点上不得不说Jquery做的很好,Jquery的Jsonp函数包含了对各种情况的处理,还伪造了一个http状态码的返回。
jsonp和ajax不同 是拿不到状态码的,但是jQuery对于所有的错误都赋予了一个404的状态码对比其他的组件库(axios-jsonp, axios-jsonp-pro, jsonp, fetch=jsonp-es6), 要不就是完全没有对超时的处理,要不然就是把错误和超时混成一谭,更有甚者,有些都不能自定义callback函数的名字。这简直太过分了。
为什么我们不选择jQuery呢 : 因为太大了webpack引入jQuery后瞬间增大了80k,单独将jsonp打包出来也有70k,问我的源码只有20k,这是我无法接受的

  1. JQuery ajax
有了原生JS的对比,jQuery更是简单
// 引入 jQuery
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script>
    $.ajax({
        url: 'http://127.0.0.1:8080/hahaha',  //发送请求
        type: 'get',
        dataType:'jsonp', // 请求方式为 jsonp
        success: function (res) {  // 执行回调函数
            console.log(res)
        }
    });
</script>
  1. vue.js
1、安装 vue jsonp        # npm install jsonp
2、在main.js中导入vue-jsonp        # 导入jsonp   import VueJsonp from 'vue-jsonp'  封装jsonp vue.use(VueJsonp)
3、编写代码  格式:  this.$http.jsonp('url',[可选参数,使用{}传参]).then(成功回调函数,失败回调函数);

还有一个关于创建公共方法jsonp.js : vue_music:JSONP

二、 跨域资源共享(CORS)

跨来源资源共享(CORS)是一份浏览器技术的规范,提供了Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,是JSONP 模式的现代版。与JSONP 不同,CORS 除了GET 要求方法以外也支持其他的HTTP 要求。用CORS 可以让网页设计师用一般的XMLHttpRequest,这种方式的错误处理比JSONP 要来的好。另一方面,JSONP 可以在不支持CORS 的老旧浏览器上运作。现代的浏览器都支持CORS。所以只要后端实现CORS就实现了跨域

虽然设置CORS和前端没有什么关系,但是通过这种方式解决问题的话会在发送请求时出现两种情况:简单请求 、复杂请求

  • 简单请求: 满足以下两大条件就属于简单请求

    请求方式GETPOSTHEAD 只请求页面首部
    请求头信息:Content-Type的值仅限于三者之一: text/plain 纯文本multipart/form-dataapplication/x-www-form-urlencoded
    注意:请求中的任意XMLHttpRequestUpload对象均没有注册任何时间监听器;XMLHttpRequestUpload对象可以使用XMLHttpRequest.upload属性访问

  • 复杂请求

不属于简单请求的都是复杂请求
复杂请求在正式通信前都会先发送预检 ,该请求是option方法,通过该请求来指导服务端是否允许跨域请求. option 利用中间件加响应头解决跨域问题

from django.utils.deprecation import MiddlewareMixin


class MyCore(MiddlewareMixin):  # 解决跨域问题
    def process_response(self, request, response):
        response["Access-Control-Allow-Origin"] = '*'  # 解决简单请求的跨域问题
        if request.method == 'OPTIONS':  # 复杂请求会发送option预检;解决复杂请求的跨域问题
            response["Access-Control-Allow-Headers"] = 'Content-Type'
            response["Access-Control-Allow-Methods"] = 'POST,DELETE,PUT'
        return response

对于自己前后端分离的项目,跨域只存在于测试阶段(前后端交互用json数据,这就是复杂请求),放到服务器不存在跨域问题,因为有nginx

一些常用的键值对
// 设置哪个源可以访问我
'Access-Control-Allow-Origin', origin
// 允许携带哪个头访问我
'Access-Control-Allow-Headers', 'name'
// 允许哪个方法访问我
'Access-Control-Allow-Methods', 'PUT'
// 允许携带cookie
'Access-Control-Allow-Credentials', true
// 预检的存活时间
'Access-Control-Max-Age', 6
// 允许返回的头
'Access-Control-Expose-Headers', 'name'

三 nginx反向代理

使用nginx反向代理实现跨域,是最简单的跨域方式。

正向代理和反向代理.png
图解:
在正向代理中,Proxy和Client同属于一个LAN(图中方框内),隐藏了客户端信息;
在反向代理中,Proxy和Server同属于一个LAN(图中方框内),隐藏了服务端信息;
实际上,Proxy在两种代理中做的事情都是替服务器代为收发请求和响应,不过从结构上看正好左右互换了一下,所以把后出现的那种代理方式称为反向代理了。
反向代理的作用:
(1)保证内网的安全,通常将反向代理作为公网访问地址,Web服务器是内网
(2)负载均衡,通过反向代理服务器来优化网站的负载

相关文章

网友评论

      本文标题:跨域问题

      本文链接:https://www.haomeiwen.com/subject/iiqhpqtx.html