浅谈Ajax跨域及其JSONP简单实现
浅谈跨域
跨域是指跨域名的访问,有三种情况:
- 域名不同的跨域。
- 域名相同、端口不同的跨域。
- 二级域名不同的跨域。
跨域问题来源于JavaScript的"同源策略",即只有 协议+主机名+端口号 (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题。
例如下面网站:
http://www.abc.com/a/b 调用 http://www.abc.com/d/c(非跨域)
http://www.abc.com/a/b 调用 http://www.def.com/a/b (跨域:域名不一致)
http://www.abc.com:8080/a/b 调用 http://www.abc.com:8081/d/c (跨域:端口不一致)
http://www.abc.com/a/b 调用 https://www.abc.com/d/c (跨域:协议不同)
值得一提的是:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
同源策略
同源策略指的是:协议+域名+端口三者皆相同,可以视为在同一个域,否则为不同域。同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
作用是一个用于隔离潜在恶意文件的重要安全机制。
所限制的跨域交互包括:
- Cookie、LocalStorage、IndexdDB 等存储内容;
- DOM 节点;
- Ajax 请求。
跨域的常见解决方法
- jsonp:只支持 GET,不支持 POST 请求,不安全 XSS
- cors:需要后台配合进行相关的设置
- postMessage:配合使用 iframe,需要兼容 IE6、7、8、9
- document.domain:仅限于同一域名下的子域
- websocket:需要后台配合修改协议,不兼容,需要使用 socket.io
- proxy:使用代理去避开跨域请求,需要修改 nginx、apache 等的配置(常规解决方案)
Ajax 其实就是向服务器发送一个 GET 或 POST 请求,然后取得服务器响应结果,返回客户端。Ajax 跨域请求,在服务器端不会有任何问题,只是服务端响应数据返回给浏览器的时候,浏览器根据响应头的Access-Control-Allow-Origin字段的值来判断是否有权限获取数据。
因此,服务端如果没有设置跨域字段设置,跨域是没有权限访问,数据被浏览器给拦截了。
所以ajax本身是不可以跨域的,通过产生一个script标签来实现跨域。因为script标签的src属性是没有跨域的限制的。
其实设置了dataType: 'jsonp'后,$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议。JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问。
通俗点来说,JSONP(JSON with Padding(填充))是 JSON 的一种“使用模式”,本质不是 Ajax 请求,是 script 标签请求。
举个栗子
这里我们使用JSONP 实现一下:
class Jsonp {
constructor(req) {
this.url = req.url;
this.callbackName = req.callbackName;
}
create() {
const script = document.createElement("script");
const url = `${this.url}?callback=${this.callbackName}`;
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
}
new Jsonp({
url: "http://127.0.0.1:8000/",
callbackName: "getMsg"
}).create();
function getMsg(data) {
data = JSON.parse(data);
console.log(`My name is ${data.name}, and ${data.age} years old.`);
}
服务端:
const http = require("http");
const querystring = require("querystring");
const server = http.createServer((req, res) => {
const url = req.url;
const query = querystring.parse(url.split("?")[1]);
const { callback } = query;
const data = {
name: "yunqinanhai",
age: "18"
};
res.end(`${callback}('${JSON.stringify(data)}')`);
});
server.listen(8000);
前端利用 http-server -p 8001 .
,开启一个服务,然后 Node 也开启一个端口为 8000 的服务,运行:
My name is yunqinanhai, and 18 years old.
一个 JSONP 的步骤实质
客户端发送 script 请求,参数中带着处理返回数据的回调函数的名字 (通常是 callback),如请求 script 的 url 是:
http://127.0.0.1:8000/?callback=getMsg
服务端收到请求,以回调函数名和返回数据组成立即执行函数的字符串,比如:其中 callback 的值是客户端发来的回调函数的名字,假设回调函数的名字是 getMsg,返回脚本的内容就是:
getMsg("{name: 'yunqinanhai', age: '18'}");
客户端收到 JavaScript 脚本内容后,立即执行脚本,这样就实现了获取跨域服务器数据的目的。
很明显,由于 JSONP 技术本质上利用了 script 脚本请求,所以只能实现 GET 跨域请求,这也是 JSONP 跨域的最大限制。
由于 server 产生的响应为 json 数据的包装(故称之为 jsonp,即 json padding),
形如:getMsg("{name: 'yunqinanhai', age: '18'}")
总结
JSONP 请求本质上是利用了 “Ajax 请求会受到同源策略限制,而 script 标签请求不会” 这一点来绕过同源策略。
- 点赞
- 收藏
- 关注作者
评论(0)