一、同源政策
什么是同源?如果两个页面拥有相同的协议、域名和端口,那这两个页面就属于同源页面,如果有其中一项不同,就是不同源。
同源政策的目的是为了保证用户的信息安全,防止恶意网站窃取数据。最初的同源政策是指 A 网站在客户端设置的 Cookie,B网站是不能访问的。随着互联网的发展,同源政策也越来越严格,在不同源的情况下,其中有一项规定就是无法向非同源地址发送Ajax 请求,如果请求,浏览器就会报错。
简单来说,就是Ajax 只能向自己的服务器发送请求。比如现在有一个A网站、有一个B网站,A网站中的网页只能向A网站服务器中发送 Ajax 请求,B网站中的网站只能向 B 网站中发送 Ajax 请求,但是 A 网站是不能向 B 网站发送 Ajax请求的,同理,B 网站也不能向 A 网站发送 Ajax请求。
二、跨域解决方案
1、概念
由于同源政策的存在,我们无法访问不同源服务器的数据,称为跨域问题。但在开发时我们很多时候需要访问不同源服务器的数据,因此需要解决跨域问题,使页面能够访问不同的服务器。
为什么跨域是由于浏览器的限制,但解决跨域却都需要服务器的配合呢?因为同源政策还有一个重要目的,就是为了保障服务器的权益,保护其数据隐私,只有得到该不同源服务器的许可,浏览器才能访问到该服务器的数据,类似于保护知识产权。如果没有同源政策的限制,任何人只要拿到了某一服务器的接口地址,就可以发请求获取该服务器的数据,这样就会出大问题。
接下来我们介绍几种常用的解决方案。
2、JSONP 解决方案
概念:
jsonp 是 json with padding 的缩写,其实并不属于 ajax 请求,而是利用 script 标签模拟 ajax 请求,只支持get请求,但兼容老版本浏览器。服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数。客户端也要定义全局作用域函数,接收数据,并在函数内部对服务器返回的数据 进行处理
案例代码:
<button id="btn">点我发送请求</button>
<script>
// 客户端定义全局作用域函数 内部对服务器返回的数据 进行处理
function fn2 (data) {
console.log('客户端的fn函数被调用了')
console.log(data);
}
</script>
<script type="text/javascript">
// 获取按钮
var btn = document.getElementById('btn');
// 为按钮添加点击事件
btn.onclick = function () {
// 创建script标签
var script = document.createElement('script');
// 设置src属性
script.src = 'http://localhost:3001/better?callback=fn2';
// 将script标签追加到页面中
document.body.appendChild(script);
// 为script标签添加onload事件
script.onload = function () {
// 将body中的script标签删除掉
document.body.removeChild(script);
}
}
</script>
封装 jsonp方法:
为了方便 jsonp 的使用,我们将其封装成一个方法,在需要时,直接调用传参即可。
function jsonp (options) {
// 动态创建script标签
var script = document.createElement('script');
// 拼接字符串的变量
var params = '';
for (var attr in options.data) {
params += '&' + attr + '=' + options.data[attr];
}
// myJsonp0124741
var fnName = 'myJsonp' + Math.random().toString().replace('.', '');
// 它已经不是一个全局函数了
// 我们要想办法将它变成全局函数
window[fnName] = options.success;
// 为script标签添加src属性
script.src = options.url + '?callback=' + fnName + params;
// 将script标签追加到页面中
document.body.appendChild(script);
// 为script标签添加onload事件
script.onload = function () {
document.body.removeChild(script);
}
}
// 调用
jsonp({
// 请求地址
url: 'http://localhost:3001/better',
data: {
name: 'lisi',
age: 30
},
success: function (data) {
console.log(456789)
console.log(data)
}
})
3、CORS 跨域资源共享
概念:
CORS 全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 同源政策的限制。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。在服务器拦截所有请求,并设置响应头(Access**-Control-Allow-**Origin)的值。
详细请浏览:http://www.ruanyifeng.com/blog/2016/04/cors.html
案例代码:
// node 服务器端代码
// 拦截所有请求
app.use((req, res, next) => {
// 1.允许哪些客户端访问我
// * 代表允许所有的客户端访问我
// 注意:如果跨域请求中涉及到cookie信息传递,值不可以为*号 比如是具体的域名信息
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
// 2.允许客户端使用哪些请求方法访问我
res.header('Access-Control-Allow-Methods', 'get,post')
next();
});
// 浏览器端代码 正常使用 ajax 请求即可
<button id="btn">点我发送请求</button>
<script src="/js/ajax.js"></script>
<script>
// 获取按钮
var btn = document.getElementById('btn');
// 为按钮添加点击事件
btn.onclick = function () {
ajax({
type: 'get',
url: 'http://localhost:3001/cross',
success: function (data) {
console.log(data)
}
})
};
</script>
4、服务器请求转发(反向代理)解决方案
概念:
同源政策是浏览器给予 ajax 技术的限制,不能请求不同源的服务器,但是服务器没有这个限制。所以我们可以通过一个中间的代理服务器,进行请求转发,将客户端的请求转发到真正的目标服务器,此时客户端只知道代理服务器的地址,而不知道目标服务器的地址,代理服务器代理的是目标服务器,所以称为反向代理。
整个过程为:由客户端发起对代理服务器的请求,代理服务器在中间将请求转发给目标服务器,目标服务器将结果返回给代理服务器,代理服务器再将结果返回给客户端。
案例代码:
// node 服务器代码
// A 服务器
// 向其他服务器端请求数据的模块
const request = require('request');
app.get('/server', (req, res) => {
request('http://localhost:3001/cross', (err, response, body) => {
res.send(body);
})
});
// B服务器
app.get('/cross', (req, res) => {
res.send('ok')
});
// 前端代码
<button id="btn">点我发送请求</button>
<script src="/js/ajax.js"></script>
<script>
// 获取按钮
var btn = document.getElementById('btn');
// 为按钮添加点击事件
btn.onclick = function () {
ajax({
type: 'get',
url: 'http://localhost:3000/server',
success: function (data) {
console.log(data);
}
})
};
</script>
5、正向代理解决方案
当客户端主动使用代理服务器,向目标服务器发起请求时,此时的代理的是客户端,所以称为正向代理。使用正向代理时,客户端是需要主动配置代理服务的地址、端口、账号密码(如有)等信息才可生效。
常见的正向代理方式有:VPN、梯子、nginx、工程化项目的proxy等。
三、withCredentials 属性
1、概念
在使用 ajax 技术发送跨域请求时,默认情况下是不会携带 cookie 信息的。而 withCredentials 属性就是用来指定,在请求时是否携带 cookie 信息,默认为 false 。而且需要在服务器段设置 Access-Control-Allow-Credentials 的值为 true ,允许客户端发送请求时携带 cookie。
2、使用
// node 服务器端代码
// 拦截所有请求
app.use((req, res, next) => {
// 允许客户端发送跨域请求时携带cookie信息
res.header('Access-Control-Allow-Credentials', true);
next();
});
// 前端代码
// 为登录按钮添加点击事件
loginBtn.onclick = function () {
// 将html表单转换为formData表单对象
var formData = new FormData(loginForm);
// 创建ajax对象
var xhr = new XMLHttpRequest();
// 对ajax对象进行配置
xhr.open('post', 'http://localhost:3001/login');
// 当发送跨域请求时,携带cookie信息
xhr.withCredentials = true;
// 发送请求并传递请求参数
xhr.send(formData);
// 监听服务器端给予的响应内容
xhr.onload = function () {
console.log(xhr.responseText);
}
}