# 浏览器跨域解决方案
# 什么是跨域
当一个请求url的:协议、域名、端口三者之间任意一个与当前地址不同的时候,就是跨域
| URL (http://www.a.com/a.html) | 说明 | 是否允许通信 |
|---|---|---|
| http://www.a.com/b.js | 同一域名下 | 允许 |
| http://www.a.com/script/b.js | 同一域名下不同文件夹 | 允许 |
| http://www.a.com:8000/b.js | 同一域名,不同端口 | 不允许 |
| https://www.a.com/b.js | 同一域名,不同协议 | 不允许 |
| http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 |
| http://script.a.com/b.js | 主域相同,子域不同 | 不允许(cookie这种情况下也不允许访问) |
| http://a.com/b.js | 同一域名,不同二级域名(同上) | 不允许(cookie这种情况下也不允许访问) |
| http://www.a.com/b.js | 不同域名 | 不允许 |
# 跨域的时候浏览器为啥会报错
为了保护用户安全上网,避免浏览器受到XSS(跨站脚本攻击)、CSRF(跨站点请求伪造)等攻击。 基于浏览器的同源策略限制:协议、域名、端口号三者相同即为同源。浏览器会拒绝跨域请求。
出现跨域问题,一般会这么报错:
# 跨域会导致什么情况
- Cookie、LocalStorage 和 IndexDB 无法读取(操作)
- DOM 和 JS 对象无法获取(操作)
- Ajax请求发送不出去
# 🤜 解决方法——JSONP
像img、script等带有src属性的标签是没有跨域限制的,所以我们可以动态创建script标签,通过src属性来进行跨域请求的来源,再用一个回调函数处理数据,但只能用于GET。
- 使用script标签发送请求
- 在src给服务器传递一个callback
- callback 的值对应到页面一定要定义一个全局函数(为什么是全局?因为服务端接收到callback函数后会返回页面中的script中去找,如果不写在全局作用域中根本找不到)
- 服务端返回的是一个函数的调用。调用的时候会吧数据作为参数包在这个函数里面。
//动态创建script
var url = "http://banana6hz.com/jsonp?callback=showData";
var script = document.createElement('script');
script.setAttribute('src', url);
//向头部输入一个脚本,该脚本发起一个跨域请求
document.getElementsByTagName('head')[0].appendChild(script);
<script type="text/javascript">
//回调函数
function showDate(result){
console.log(result);
}
</script>
再来看看jQuery如何实现jsonp调用
<script type="text/javascript">
jQuery(document).ready(function(){
$.ajax({
type:"get",
async:false,
url:"",
dataType:"jsonp",
jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonCallback:"showDate",//自定义的jsonp回调函数名
success:function(json){
//对请求到的数据进行处理
console.log(jsonp)
},
error:function(){
alert('error')
}
});
</script>
# 🤜 解决方法——CORS(跨域资源共享)
W3C推出的了一个标准----"跨域资源共享"(Cross-origin resource sharing),简称CORS。该标准定义了跨域访问资源时服务器和浏览器怎么通信。
实现原理:浏览器一旦发现跨越请求,就会自动添加一些头信息和服务器进行沟通,来确定跨域请求通不通过。
现在除IE10以下的浏览器都支持这个标准。
服务器设置Access-Control-Allow-Origin就可以开启CORS。该属性表示那些域名可以访问资源。如果设置通配符则表示所有网站都可以访问资源。
浏览器会把跨域请求分成两类:简单和非简单请求。
简单请求
- 请求方法为:post、get、head
- Content-Type的值仅限为以下三种之一:
text/plainmultipart/form-dataapplication/x-www-form-urlencoded
复杂请求:除简单请求以外的其他请求
实现方法
简单请求
- 浏览器发出CORS请求,在头信息添加Origin字段,字段说明本次请求来自哪个源(协议+端口+域名)
- 服务器判断Origin指定的源在不在许可范围内。
- 如果在,服务器返回的响应,会多出几个头部信息。
Access-Control-Allow-Origin: http://banana.com //值为Orign字段的值,或者*,表示接受任何域名的请求 Access-Control-Allow-Credentials: true //布尔值,表示是否允许浏览器发送cookie给服务器 Access-Control-Expose-Headers: FooBar //表示可以通过getResponseHeader('FooBar')获FooBar字段的值 Content-Type: text/html; charset=utf-8- 如果不在,服务前返回一个正常的HTTP响应,浏览器发现回应的头部信息里没有Access-Control-Allow-Orign字段,抛出错误。
复杂请求:复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求。
- 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。
//浏览器js脚本 var url = 'http://banana.com/cors'; var xhr = new XMLHttpRequest(); xhr.open('PUT', url, true); xhr.setRequestHeader('X-Custom-Header', 'value'); xhr.send(); //浏览器发出预检请求 OPTIONS /cors HTTP/1.1 //Option表示这个请求是用来询问的 Origin: http://banana.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: banana.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...- 肯定答复,浏览器发出正式的XMLHttpRequest请求
- 拒绝,报错
XMLHttpRequest cannot load http://banana.com. Origin http://banana.com is not allowed by Access-Control-Allow-Origin.
# 🤜 解决方法——document.domain
该方法只能只用于二级域名相同的情况下。
'a.banana.com`
`b.nanana.com`
document.domain = banana.com
这样他们就可以跨域访问数据了