浏览器的同源策略——跨域问题的由来
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
例如,下表给出了相对http://store.company.com/dir/page.html同源检测的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 成功 | |
http://store.company.com/dir/inner/another.html | 成功 | |
https://store.company.com/secure.html | 失败 | 不同协议 ( https和http ) |
http://store.company.com:81/dir/etc.html | 失败 | 不同端口 ( 81和80) |
http://news.company.com/dir/other.html | 失败 | 不同域名 ( news和store ) |
同源策略是指浏览器限制了从同一个源加载的文档或脚本与来自另一个源的资源进行交互,是一个用于隔离潜在恶意文件的重要安全机制。具体来说,同源策略分为以下两种:
- DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
- XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。
浏览器的同源策略导致跨域,只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。
JSONP跨域
基本思想
jsonp跨域利用了Web页面上js文件的调用不受浏览器同源策略的影响这一特性,使用web页面的<script>
标签实现跨域请求。
- 客户端嵌入
<script>
标签,将src属性设置为要请求资源的URL,并将回调函数作为参数添加到URL后。需要注意的是,因为要作为src属性的参数,此回调函数必须为全局对象。 - 假设用户想访问 http://127.0.0.1:3000?jsonp=callback , 希望期望返回JSON数据[data1,data2], 真正返回到客户端的数据显示为callback([data1,data2])。
基于node.js的jsonp跨域实例
搭建前端页面,该页面向http://127.0.0.1:3000请求数据,并将回调函数设置为jsonpCallback。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
function jsonpCallback(data) {
alert('获得 X 数据:' + data.x);
}
</script>
<script src="http://127.0.0.1:3000?callback=jsonpCallback"></script>
</head>
<body>
this is 8080 server page!
</body>
</html>
使用node.js,将上述前端页面部署到本地的8080端口。
const url = require('url');
const http = require('http');
var fs = require('fs');
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/html'});
fs.createReadStream('index.html').pipe(res);
}).listen(8080, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:8080');
使用node.js搭建jsonp服务器,端口为3000,服务器将data数据作为回调函数的参数返回给前端页面。
//server.js
const url = require('url');
const http = require('http');
http.createServer((req, res) => {
const data = {
x: 10
};
const callback = url.parse(req.url, true).query.callback;
res.writeHead(200);
res.end(callback+'('+JSON.stringify(data)+')');
// res.end(`${callback}(${JSON.stringify(data)})`);
}).listen(3000, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:3000');
在浏览器中输入 http://127.0.0.1:8080即可打开前端页面,并看到前端页面通过跨域请求从3000端口处获得的数据。
JSONP跨域的优缺点
优点
1. 不受浏览器同源策略的影响;
2. 兼容性较好,支持老版本的浏览器;
3. 能利用回调函数处理返回的数据。
缺点
1. 只支持GET请求,不支持POST等其它HTTP请求;
2. 它只支持跨域HTTP请求这种情况,不能解决异域页面或 iframe 之间进行数据通信的问题。
CORS跨域
基本思想
CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。
CORS 需要浏览器和服务器同时支持才可以生效。实现了 CORS 接口的浏览器在发现 ajax 请求跨源时,会自动添加一些附加的头信息(Origin字段),有时还会多出一次附加的请求(预检请求),但用户不会有感觉。实现了 CORS 接口的服务器会在HTTP请求头中增加一系列HTTP请求参数(例如Access-Control-Allow-Origin等),来限制哪些域的请求和哪些请求类型可以接受,参数可以在代码里写,也可以写在服务器配置文件里。目前,所有浏览器都支持该功能(IE浏览器不能低于IE10)。对于开发者来说,CORS 通信与同源的 ajax 通信没有差别,代码完全一样。
使用node.js实现COR跨域实例
新建前端页面,在前端页面中向http://127.0.0:8080发送XHR请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:3000', true);
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText);
}
}
xhr.send(null);
</script>
</body>
</html>
将上述前端页面部署到http://127.0.0:8080。
const url = require('url');
const http = require('http');
var fs = require('fs');
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/html'});
fs.createReadStream('index.html').pipe(res);
}).listen(8080, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:8080');
新建服务器,监听本地3000端口。设置服务器的请求头,允许本地的8080端口进行跨域访问。
require('http').createServer((req, res) => {
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://localhost:8080',
'Content-Type': 'text/html'
});
// res.writeHead(200, {'Content-Type': 'text/html'});
res.end('这是你要的数据:1111');
}).listen(3000, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:3000');
在浏览器中输入http://localhost:8080即可打开前端页面,并显示跨域得到的数据。
cors跨域成功的关键在于服务器头信息的 Access-Control-Allow-Origin 字段中是否包含请求页面的域名。如果服务器没有设置 Access-Control-Allow-Origin 字段,或该字段中不包含请求页面的域名,则会发生跨域错误。
CORS的优缺点
优点
1. 使用简单方便
2. 支持所有类型的HTTP请求
缺点
1. CORS 是一种新型的跨域问题的解决方案,存在兼容问题,仅支持 IE 10 以上
CORS与JSONP的比较
CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。
JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
Server Proxy(服务器代理)跨域
基本思想
浏览器由于同源策略会产生跨域问题,但服务器之间不存在跨域问题。服务器代理即,当客户端需要跨域的请求时先发送请求给后端,由后端请求所要域的资源再返回给客户端。