跨域访问 - 跨域请求
同源策略
适用于浏览器的一种资源访问策略;
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
浏览器为什么使用同源策略
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持 JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名/IP/主机,协议,端口相同。在浏览器的js中,通过代码(脚本)去访问网络资源的时候会应用该策略
假设在a页面的地址是(http://www.baidu.com/a.html),
那么a页面所在的源信息是:协议:http,域名:baidu.com,端口:80;
那么在这个页面中通过js去访问另外一个资源:(http://www.baidu.com/b.html)。
那么这个时候会使用同源策略进行检测,上面两个页面的协议、域名、端口是相同的,那么这时候一个同源请求,如果访问的是:(http://www.qq.com/b.html),那么很明显这个时候就是非同源请求,这个时候,请求会受到一定的限制。
以 http://www.baidu.com/a.html 为例,以下都是非同源的:
- http://www.qq.com/b.html
- http://www.baidu.com:8080/b.html
- http://baike.baidu.com/b.html
- https://www.baidu.com/b.html
- http://baidu.com/b.html 注意:www其实也是一个二级子域名,只是习惯把www和顶级域名绑定在一起去使用而已
AJAX请求
我们使用ajax去请求资源的时候,就被使用同源策略进行检测,同源策略是适用于浏览器的,也就是说如果我们发送了一个跨域的请求,服务器是能接收到并能处理和返回的,但是浏览器在接收到返回数据以后,会比较他们的域是否相同,如果不相同,拒绝接收和处理!
解决办法
Access-Control-Allow-Origin
当浏览器接收到非同源数据的时候,会首先去头信息看Access-Control-Allow-Origin字段里面的值,如果当前域在Access-Control-Allow-Origin里面有包含,则忽略同源策略
例如我们有给服务器分别为localhost:7777 和 localhost:8888;
当我们在端口8888情况下访问7777的数据时,因为同源策略检测,ajax请求就会直接报错,这时我们需要在被请求也就是8888的后端里设置头信息res.setHeader('Access-Control-Allow-Origin',"localhost:7777")
,
感觉类似白名单一样,如果想所有不同源的端口都访问可以把里面的值改为通配符()*
后端代理
后端之间互相访问请求不同源的是不会有同源策略,因为同源策略是基于浏览器来产生的,所以我们可以让自己的服务器后端去访问请求跨域的地址,然后把结果再传给我们,这样就类型一种代理的行为
//nodejs中axios插件 就集成了服务端去发送请求方法
app.get('/data', (req, res) => {
/*
* 通过服务端去发送请求
* */
axios.get('http://localhost:8888/data').then( response => {
//console.log(response.data);
res.send(response.data);
} );
});
//然后ajax在去接收responseText
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://localhost:8888/data', true);
xhr.onload = function() {
console.log(this.responseText);
};
xhr.send();
jsonp
为了解决同一个接口的调用不同的函数,实现不同的逻辑,那么后端输出的函数名不再固定,而是由前端通过get方式传入一个指定的参数,比如callback,后端根据callback传入的值,输出不同的函数调用名
弊端:只能通过get方式
注意:一个api接口是否能够通过jsonp的方式去使用,还要看该接口输出的数据格式,知否能够被script标签加载后被js所执行
具体方法:通过创建一个script标签,地址指向第三方的API网址,例如被请求方的数据是:
app.get('/list', (req, res) => {
let callback = req.query.callback || 'fn';
let type = req.query.type || 'teachers';
let data = {
teachers: ['Leo','Motao', 'zMouse'],
students: ['张三','李四', '王五', '赵六', '田七']
};
res.send(callback + '('+ JSON.stringify(data[type]) +')');
});
我们通过创建一个script标签,就不会收到同源策略的影响,其中可以通过一个callback函数来接受数据,可以是一个约定的函数名,或者通过地址来传递,这样我们前台在拿数据就可以通过函数名来拿到手
document.onclick = function() {
var scriptElement = document.createElement('script');
scriptElement.src = 'http://localhost:7777/list?callback=fn2&type=students';
//需要什么信息就传入什么约定好的名字
document.body.appendChild(scriptElement);
};
function fn(data) { //地址中传入的callback名
var html = '';
data.forEach( item => {
html += '<li>'+ item +'</li>';
} );
uls[0].innerHTML = html;
}
网友评论