为啥要跨域?
那必须得从同源策略说起了
什么是同源
window.origin或location.origin可以得到当前 源- 源=协议+域名+端口号
- 如果两个url的协议 域名 端口号
- 完全一致,那么这两个url就是同源
- 举例
同源策略定义
- 不同源的页面之间,不准互相访问数据
- 浏览器规定:如果js运行在A里,那么就只能获取A的数据
- 不能获取源B的数据,即不允许跨域
- 这就是浏览器故意设计的功能,就是为了保护用户隐私而设计的
- 举例
如何解决同源策略的限制呢
方法一:CORS
- CORS 是跨域资源共享的缩写
- 该技术通过在目标域名返回 CORS 响应头来达到获取该域名的数据的目的
- 改技术核心就是设置 response header,分为简单请求和复杂请求两种
- 简单请求只需要设置 Access-Control-Allow-Origin: 目标源 即可,复杂请求则分两步走,第一步是浏览器发起 OPTIONS 请求,第二步才是真实请求。OPTIONS 请求需要把服务器支持的操作通过响应头来表明,如 Access-Control-Allow-Methods: POST, GET, OPTIONS,另外一个重要的响应头是 Access-Control-Allow-Credentials: true 用来表明是否接受请求中的 Cookie。
- 优点是通过简单的配置就能跨域
- 缺点是某些古老浏览器不支持 CORS 或不支持 Credentials
- 解决办法是用 JSONP 或 P3P (这个目前没有研究)等技术
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS</title>
</head>
<body>
<script>
const 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>
- 这似乎跟一次正常的异步 ajax 请求没有什么区别,关键是在服务端收到请求后的处理:
require('http').createServer((req, res) => {
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://localhost:8080'
});
res.end('这是你要的数据:1111');
}).listen(3000, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:3000');
- 关键是在于设置相应头中的 Access-Control-Allow-Origin,该值要与请求头中 Origin 一致才能生效,否则将跨域失败。
解决方法二:JSONP
- JSONP 是 json with padding 的缩写
- 该技术通过 script 不受同源策略限制来达到跨域的目的
- 该技术核心是前端构造 script 发起 get 请求,后端将数据放到 js 回调里,前端接受响应后执行回调拿到数据
- 是通过简单的约定就能跨域
- 是不支持 get 以外的动词,而且存在 csrf 风险
- 决办法是 CORS 或 csrf token
- 代码示例:
- 后端逻辑
//server.js
const url = require('url');
require('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)})`);
}).listen(3000, '127.0.0.1');
console.log('启动服务,监听 127.0.0.1:3000');
- 通过 node server.js 启动服务,监听端口 3000,这样服务端就建立起来了前端页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index.html</title>
</head>
<body>
<script>
function jsonpCallback(data) {
alert('获得 X 数据:' + data.x);
}
</script>
<script src="http://127.0.0.1:3000?callback=jsonpCallback"></script>
</body>
</html>