1. 同源策略
1.1 定义
所谓同源是浏览器的一种安全策略:若两个地址的域名
、协议
、端口
相同,则它们是同源的。
域名:网站ip的唯一标识,如www.baidu.com,www.qq.com
协议:浏览器连接并访问资源的遵从的统一约定。主要为http和https两种。
端口:不同的端口对应不同的应用程序,http网站首页一般默认端口为80端口,https则为443。
示例(以http://www.example.com/list.html
为对比地址):
URL | 是否同源 |
---|---|
http://test.example.com/list.html | 否,域名不同 |
https://www.example.com/list.html | 否,协议不同 |
http://www.example.com:8080/list.html | 否,端口不同 |
http://www.example.com/detail.html | 是 |
1.2 产生原因和影响
同源策略存在于浏览器端是一种约定,用来保护浏览器的数据安全和用户的隐私安全。如果没有同源策略,A网站可以随意访问B网站的Cookie等信息是不安全的,现在所有支持JavaScript 的浏览器都会使用这个策略。
2. 跨域
2.1 跨域限制
不同源的地址之间无法通过ajax,fetch等途径进行访问。这种请求称之为跨域请求
。但是对于图片、js脚本、css资源这种静态资源文件还是可以通过对应的img标签,script标签和link标签跨域进行调用。
此外,浏览器的Cookie、WebStorage 和 IndexedDB数据也无法跨域读取。
2.2 跨域解决方案
2.1.1 CORS跨域资源共享(主流方案)
CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通**(需要客户端和服务端协同处理)**。
CORS允许浏览器向跨域服务器发出XMLHttpRequest请求,突破了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持,目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。CORS原理:浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。只要服务器实现了CORS接口,就可以跨源通信。
浏览器将跨域请求分为两类,浏览器对这两种请求的处理,是不一样的。
1:简单请求(同时符合下面两种情况)
请求方法是下列之一:
- GET
- HEAD
- POST
HTTP请求头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
简单请求时,浏览器会直接发送跨域请求,并在请求头中携带Origin
并赋值为当前域名,表明这是一个跨域的请求。
服务器端接到请求后,会根据自己的跨域规则,通过Access-Control-Allow-Origin
(允许的跨域请求域名)和Access-Control-Allow-Methods
(允许的跨域请求方式)响应头,来返回验证结果。如果验证成功,则会直接返回访问的资源内容。
如果请求时Origin指定的源,不在服务端设置的对应响应头的许可范围内,一般服务器会返回一个403 Forbidden的HTTP响应和控制台错误。
2:非简单请求(不属于简单请求的跨域请求)
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。浏览器发出的Preflighted requests是一个OPTION
请求
OPTIONS
请求头部中会包含以下头部:
Origin
:表示请求来自哪个源。Access-Control-Request-Method
:必填,用来列出浏览器的CORS请求会用到哪些HTTP方法。Access-Control-Request-Headers
:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
服务器收到"预检(OPTIONS)"请求以后,检查了Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
字段以后,设置比如Access-Control-Allow-Method
、Access-Control-Allow-Headers
头部与浏览器沟通来判断是否允许这个请求。
如果Preflighted requests验证通过,浏览器才会发送真正的跨域请求。
如果Preflighted requests验证失败,则会返回403状态,浏览器不会发送真正的跨域请求。
2.1.2 JSONP
JSONP(json with padding)方式, 通过script标签请求资源,允许用户在scr地址中传递一个callback参数(callback=abc)即将预先定义好的回调函数名以查询字符串的形式传递给服务端,服务端收到请求后会将要返回的数据用这个callback参数(abc)包裹住再返回,即将请求传入的参数abc作为函数名来包裹住要返回的JSON数据,比如abc(JSON),这样客户端在收到服务端返回的abc(JSON)文件后默认用JS解析执行。通过JSONP就可以随意定制自己的函数来自动处理返回数据了。
示例:
原理:
页面虽然不允许发起跨域的ajax
请求,但引用不同域名的js
脚本是可行的。
- 执行跨域的ajax请求时会自动发起一个Script请求,请求文件名为callback=jQueryxxx,jQueryxxx是jquery随机生成的一个回调函数名称。
- 该次请求返回来的结果则是jQueryxxx()函数调用字符串,执行这个函数完成跨域请求。
一句话来概括就是,通过动态创建script标签,然后利用 src 属性进行跨域,而每一次跨域就是一个script脚本的引入。
缺点:
1.安全问题,src引用是开放的,所以jsonp的资源都被所有人访问到。解决方法是用jsonp中的token参数,通过A域和B域共用同一套cookie来验证A的身份。
2.只能用GET方式不能用POST方式获取数据即只能读不能写。
3.可被注入恶意代码如?callback=alert(1); 这问题只能用正则过滤字符串的方法解决,过滤callback后的内容不能有括号之类的条件。
2.3 withCredentials 属性
默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。
使用方法:
- 客户端必须在
AJAX
请求中打开withCredentials属性 - 服务器端设置响应头
Access-Control-Allow-Credentials: true
- 服务器端设置响应头
Access-Control-Allow-Origin
的值来指定允许跨域的域名且必须为一个确定域名,而不能使用*这样的通配符。