跨域(转)
转载自:https://www.jianshu.com/p/827b041be446
https://segmentfault.com/a/1190000009773724
https://segmentfault.com/a/1190000013536703
跨域是什么,为什么会有跨域?跨域的解决方法是什么?常用的是什么?原理是什么?
什么是跨域?
跨域是指从一个域名去请求另一个域名的资源。严格来说,只要协议,域名、端口任何一个不同,就视为跨域。
为什么会出现跨域?
为了安全起见,浏览器设置了一个同源策略,规定:只有域名,端口,协议全部相同,才叫做同源。当页面在执行一个脚本时,会检查访问的资源是否同源,如果不是,就会报错。可是在实际开发中,经常会有跨域加载资源的需求,避免不了跨域请求,所以就出现了跨域。
什么是同源策略及限制?
同源策略是指从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是一个用来隔离潜在恶意文件的的关键的安全机制。为了安全起见,浏览器限制了不同源之间的交互。(Cookie包含大量的登录信息,如果一个网页可以跨域访问另一个网站的Cookie,那么不法分子跨域访问到用户的Cookie后就可以为所欲为,太可怕了!)
跨域限制的资源?
(1)数据存储限制:无法跨域读取 cookie、LocalStorage和IndexDB
(2)脚本 API 限制:无法获取 Dom
(3)网络请求限制:XHR 请求无法接收响应(可以正常发送请求,但是浏览器会拒绝接受响应)
发生跨域时,允许进行的操作
- 通常允许跨域写操作(link、redirect、表单提交)
- 通常允许跨域资源嵌入(script、img、video...)
JS中自带跨域技能的标签是什么 ?
<img/>、<script><script/>以及<iframe>标签,不受同源策略的影响。可以借助这一特点,实现跨域。
常用的跨域发送ajax方法是什么?
1、JSONP:通过动态添加一个script标签,向服务器请求脚本,脚本中一般包含一个客户端定义的函数。将响应数据作为参数,调用回调函数。
<script src="http://localhost:3000/api/orderlist?msg=hello&callback=jsoncb"></script>
原理:利用了script标签,在标签外套了一层壳,利用标签特性达到跨域加载资源的效果。
JSONP由两部分组成,回调函数和数据。
优点:
(1)兼容性好,在多古老的浏览器都能运行。
(2)能直接访问响应文本,支持在浏览器与服务器之间双向通信。
缺点:
(1)只支持GET请求,不支持POST请求;
(2)不够安全。因为JSONP是从其他域中加载代码执行,如果其他域不安全,可能会在响应中带有恶意代码。
(3)不容易确认请求是否失败。
2、CORS -- 跨站资源共享,它是跨域的官方解决方案,升级版的JSONP。
原理是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。默认情况下,请求和响应都不包含cookie信息。
CORS需要浏览器和后端同时支持,实现CORS通信的关键是后端,只要后端实现了CORS,就实现了跨域。浏览器设置Origin字段,服务端设置Access-Control-Allow-Origin 就可以开启CORS,该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
浏览器将ajax请求分为两类: 简单请求 & 非简单请求
2.1 简单请求
简单请求就是满足以下两个条件的请求:
- 请求方法为HEAD、GET和POST
- HTTP请求头只包含:
Accept
、Accept-Language
、Content-Language
、Last-Event-ID, 此外,
Content-Type的值只能是
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
三者之一
对于简单请求,浏览器可以直接发送请求到服务器,但是会在请求头中添加一个origin
字段(该字段用来说明请求的来源)。服务器会识别该字段,判断是否允许跨域。
如果允许跨域,服务器会返回结果并在响应头上添加三个字段:
- 1.Access-Control-Allow-Origin
该字段的值可以是Origin字段的值,或者是*
,表示服务器接受任何源的跨域请求。
- 2.Access-Control-Allow-Credentials
可选字段,它表示是否允许发送Cookie。值为true
时,表示发送请求的时候允许发送Cookie,如果不包含该字段,则表示不允许发送Cookie。
值得一提的是,如果服务器允许发送Cookie,那么不允许将Access-Control-Allow-Origin
的值设为*
。
- 3.Access-Control-Expose-Headers
可选字段,在没有该字段的情况下,针对跨域请求,XHR对象的getResponseHeader()
方法只能拿到Content-Language
、Content-Type
、Cache-Control
、Expire
、Last-Modified
、Pragma
这六个字段,该字段可以设置额外可以拿到的字段。
2.2非简单请求
不满足简单请求的跨域请求都是非简单请求,比如PUT或DELETE方法。
不同于简单请求的直接向服务器请求,非简单请求会在发送之前,先进行一次“预检”(preflight),即,向服务器发出一个OPTIONS请求,查询服务器是否允许它进行跨域请求。
1.Access-Control-Allow-Origin
该字段和简单请求中的同名字段一样。
2.Access-Control-Allow-Credentials
可选字段,与简单请求中的同名字段一样。
3.Access-Control-Allow-Methods
该字段表示服务器支持跨域的所有方法,是一个逗号分隔的字符串,如:POST,DELETE。
4.Access-Control-Allow-Headers
该字段表示服务器支持的所有头信息,也是一个逗号分隔的字符串。
5.Access-Control-Max-Age
可选字段,在一段时间内,浏览器对同一个域名进行非简单跨域请求,只对第一次进行“预检”,而这一次“预检”的结果将被缓存,接下来的请求都通过该结果进行判断。该字段就是用来设置“预检”结果缓存的时间长短,可以将其值设为-1
来禁用“预检”缓存。
3、WebSocket -- 不受同源策略影响。原理是因为它不使用HTTP协议,而使用一种自定义的协议,支持跨域通信,专门为快速传输少量数据设计。
4、Iframe 注意:在使用iframe进行通信时,只有父窗口与子窗口同源才可以;如果跨域,无法拿到对方DOM.
// 现有两个页面,分别在两个域下。a页面想要向http://127.0.0.1:3001发送ajax请求,需要借助http://127.0.0.1:3001下的b页面。
http://127.0.0.1:3000/a.html
http://127.0.0.1:3001/b.html
当a页面中有一个b页面的iframe时,两个页面共享window.name。在b页面与拿到数据后赋值给window.name。可以将b页面的location.href转为http://127.0.0.1:3000下的c页面,(相当于同一个域下的c页面内嵌在a页面中) 由c页面调用a页面的回调方法。
// a.html
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <iframe src="http://127.0.0.1:3001/b.html"></iframe> <script> function print(data) { console.log(data); } </script> </body> </html>
// b.html
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="http://code.jquery.com/jquery-3.3.1.min.js"></script> </head> <body> <script> $.ajax({ url: 'data.json' }).done(data => { window.name = JSON.stringify(data); location.href = 'http://localhost:3000/c.html'; }) </script> </body> </html>
// c.html
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> window.parent.print(JSON.parse(window.name)); </script> </body> </html>
子窗口内获取父窗口DOM元素的方法:window.parent / window.top.
------------------------------------------------------------------------------------------------
服务器端:
如果你要自己实现 CORS 功能,注意遵守下面的准则:
如果是写死的 Access-Control-Allow-Origin(*或者某一特定的域名)
,一定不要加 Vary: Origin;
如果是根据 Origin
请求头动态计算出的(*.taobao.com) Access-Control-Allow-Origin
,一定要始终加上Vary: Origin
,即便在没有 Origin
请求头的情况(<img>和<script>)。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步