跨域相关的概念、造成跨域的原因、以及9大解决跨域的方案。
☀概念
源:(origin)就是协议、域名和端口号。
同源: 同源指协议、域名、端口皆相同。
同源策略Same origin policy:
同源策略(Same origin policy)是一种约定,它是由Netscape提出的一个著名的安全策略。它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。 可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略是浏览器的一个安全功能,主要时为了防止CSRF攻击【利用用户的登录专改发起恶意请求】,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,阻止用户读取到另一个域下的内容。现在所有支持JavaScript 的浏览器都会使用这个策略。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的,请求被发送出去了,但是相应被浏览器拦截。
同源策略控制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
总:
同源策略 是由Netscape提出的一个著名的安全策略,现在所有支持JavaScript 的浏览器都会使用这个策略。实际上,这种策略只是一个规范,并不是强制要求,各大厂商的浏览器只是针对同源策略的一种实现。它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
同源策略限制:
- JS中的AJAX
- cookie不能读取 (如我在自己的站点无法读取博客园用户的cookie)
- dom无法获得
- 浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
不受同源策略限制:
- 页面中的链接(浏览器输入框),重定向以及表单提交是不会受到同源策略限制的。联想到postman,soapui工具也不会受到同源影响。
- 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的
<script src="..."></script>,<img>,<link>,<iframe>
等。
前端/js跨域
受前浏览器同源策略的影响,不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域。
js跨域
是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据。只要协议
、域名(或主机地址)
、端口
有任何一个不同,都被当作是不同的域。总:
跨域 简单的来说,指的是两个资源非同源。出于安全方面的考虑,页面中的JavaScript在请求非同源的资源时就会出 跨域问题 ——即跨域请求(非同源策略请求),这时,由于同源策略,我们的请求会被浏览器禁止。也就出现了 我们常说的 跨域 问题。
举例:
1
2
3
4
5
6
7**在 a的应用的js脚本中调用了b的后端地址**
http://cas.feixue.com a
http://cart.feixue.com b
a: js
ajax请求的方式调用 -->http://cart.feixue.com/......do上述的错误信息为 不能加载该地址, 头信息
Access-Control-Allow-Origin
不存在!,为什么默认不能跨域,主要是为了安全考虑.同源策略请求 ajax / fetch(新诞生的数据通信方式)
跨域传输
gmap
开发在本地修改host可以模拟出同源的效果。
服务器拆分:
web服务器:静态资源
data服务器:业务逻辑和数据分析
图片服务器:
☀解决方法
☀1、JSONP:
JSONP需要服务器端的支持。
☀实现思路和原理:
字符串的魅力
☀Jquery实现JSONP示例:
有多种实现方式,ajax,单独jsonp模块,或者vue中的模块。
☀ajax实现
1 | $.ajax({ |
ajax dataType
:
dataType
默认类型是:String
预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在 1.4 中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。可用值:
- “xml”: 返回 XML 文档,可用 jQuery 处理。
- “html”: 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。
- “script”: 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 “cache” 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载)
- “json”: 返回 JSON 数据 。
- “jsonp”: JSONP 格式。使用 JSONP 形式调用函数时,如 “myurl?callback=?” jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。
- “text”: 返回纯文本字符串
其中,text和xml类型返回的数据不会经过处理。数据仅仅简单的将XMLHttpRequest的responseText或responseHTML属性传递给success回调函数,
‘’‘注意’’’,我们必须确保网页服务器报告的MIME类型与我们选择的dataType所匹配。比如说,XML的话,服务器端就必须声明 text/xml 或者 application/xml 来获得一致的结果。
如果指定为html类型,任何内嵌的JavaScript都会在HTML作为一个字符串返回之前执行。
如果指定script类型的话,也会先执行服务器端生成JavaScript,然后再把脚本作为一个文本数据返回。
如果指定为json类型,则会把获取到的数据作为一个JavaScript对象来解析,并且把构建好的对象作为结果返回。为了实现这个目的,他首先尝试使用JSON.parse()。如果浏览器不支持,则使用一个函数来构建。JSON数据是一种能很方便通过JavaScript解析的结构化数据。
如果获取的数据文件存放在远程服务器上(域名不同,也就是跨域获取数据),则需要使用jsonp类型。使用这种类型的话,会创建一个查询字符串参数 callback=? ,这个参数会加在请求的URL后面。服务器端应当在JSON数据前加上回调函数名,以便完成一个有效的JSONP请求。如果要指定回调函数的参数名来取代默认的callback,可以通过设置$.ajax()的jsonp参数。
☀现况:
JSONP现在已经不怎么使用了。
注意:JSONP只支持GET请求(资源文件请求),不支持POST请求。
安全问题:参数容易被截取。响应JS文本加入木马程序。
☀2、CORS跨域资源共享
☀使用前提背景
浏览器需要支持CORS
当js调用跨域被阻止时,会报如下错误:No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
这说明浏览器支持CORS,需要在服务端进行配置。
☀CORS原理
所有支持CORS的浏览器客户端,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin。发送跨域请求的时候,都会预先发送一个OPTIONS试探性请求。验证服务器端的response是否配置了CORS。
客户端只需要发送请求(ajax/fetch)即可,配置在服务器端:
服务器response的header中设置参数:
参数 | 值 | 说明 |
---|---|---|
Access-Control-Allow-Origin | * | http://127.0.0.1:3001 |
Access-Control-Allow-Methods | PUT,POST,GET,DELETE,OPTIONS,HEAD | 跨域允许的客户端请求方法。 |
Access-Control-Allow-Headers | Content-Type,Content-Length,Authorization,Accept,X-Requested | 跨域允许的客户端请求头head设置。 |
Access-Control-Allow-Credentials | true | 跨域是否允许携带资源凭证,如:cookie。 |
☀现况:
缺陷:
设置Access-Control-Allow-Origin 只能有两种,*和具体地址。
*设置了,就不能使用cookie了。**为啥不能使用cookie,浏览器为了保证安全性。
能使用cookie的只有具体地址,但只能设置一个。--------使用nginx+cors技术解决。
☀3、代理:http proxy
☀核心思想
使用后端作为中间件代替前端访问跨域的请求。
例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
☀代理工具:
1.nginx
2.webpack webpack-dev-server
☀4.post message
postMessage解决跨域、跨窗口消息传递
平时做web开发的时候关于消息传递,除了客户端与服务器传值还有几个经常会遇到的问题
页面和其打开的新窗口的数据传递
多窗口之间消息传递
页面与嵌套的iframe消息传递
上面三个问题的跨域数据传递
比较全面的说明文档:
两个不同源的Html页面代码示例:
注意iframe.onload(){}
A.html
B.html
☀5.socket.io
html5中提供的websocket协议来完成。
微信中的实时聊天技术,使用的是socket.io来玩。
客户端
服务器端
☀6. document.domain + iframe
实现同一个主域,不同子域之间页面的操作。
v.qq.com ——>qq.com
sport.qq.com ——>qq.com
俗话说的降域操作。
☀代码示例:
☀现况
只能实现同一个主域,不同子域之间的操作。限制比较多。
☀7.window.name + iframe
实现不同域之间的操作。
B.html
同源的空代理页面:
proxy.html
A.html
关键步骤,需要将iframe的地址重新指向到同源的代理页面中。
☀8.location.hash + iframe
☀代码示例
A.html
关键步骤,改变跨域路径的hash值。
B.html
关键步骤:
1. 嵌入iframe源域的c.html页面。
2. 监听hash值改变。
C.html
关键步骤:
1. 监听hash改变。
2. 通过操作同域的A的js回调。
☀现况:
通过url传数据存在局限。仅供参考。
☀9.本地测试修改host
☀场景:
接口部署在服务器,为方便本地启动测试,修改host文件,在测试时页面和接口非跨域访问。
☀总结
实现跨域的方式分类:
利用允许跨域机制
使其变成同源
☀参考资料:
- https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
- https://segmentfault.com/a/1190000015597029
- https://blog.csdn.net/tjcjava/article/details/76468225
- https://blog.csdn.net/yup1212/article/details/87633272
- https://www.cnblogs.com/itmacy/p/6958181.html
视频: