1.同源策略于跨域
1.1 同源策略
首先我认为想要了解跨域前必须先要知道浏览器的同源策略。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源指的是协议、域名、端口相同。
同源策略是客户端脚本(尤其是Javascript)的重要的安全度量标准,其目的是防止某个文档或脚本从多个不同 源装载。
其实在13年之前,在还没有提出前后端分离时。大多项目都是同源的,因为项目的前端页面不是复杂,通常直接将前端页面放在服务器上,直接进行服务器渲染。用户直接请求到html页面。
但是随着时间的推移前端界面变得越来越重要。如果将所以的前端界面放在服务器进行服务器端渲染,这无疑增加了服务器的压力。这时候就提出了前后端分离。所谓前后端分离就是前端有自己的web服务器,后端只提供数据,web服务器在另一边进行请求。这样就产生了一个问题。跨域
1.2 跨域
经过上面的描述,或许你已经基本清楚了跨域时怎么回事。
首先跨域是浏览器为了防止出现安全问题的一种机制
我们来看一下跨域的具体表现:
- 协议不同,如http, https
- 端口不同
- 主域相同,子域不同;
- 主域不同;
- IP地址和域名之间也算是跨域,浏览器不会自动做IP域名的映射
来看一个典型的跨域的例子: 现在我的服务器端口号是8000,web服务器的端口是5050。在web页面直接进行Ajax请求。会出现以下的错误内容:
读一下报错信息大概的意思就是http请求无法将端口为8000的服务器的数据读到5500端口的web界面。
1.3 为什么要设定跨域这个概念
上面也说到跨域是浏览器设定的一种机制。
细心的同学可能已经出现跨域情况时浏览器给我们的状态还是200。这就足以说明跨域是浏览器的机制,只是数据被拦截。请求时成功的。
那么到底到底为什么会有跨域呢?看一下几个场景:
1.用户登录了自己的aaa页面 aaa.com,http://aaa.com向用户…
2.用户浏览了恶意页面 bbb.com。执行了页面中的恶意AJAX请求代码。
3.bbb.com向http://aaa.com发起A… HTTP请求,请求会默认把aaa.com对应cookie也同时发送 过去。
4.aaa页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
5.而且由于Ajax在后台执行,用户无法感知这一过程。
2.解决跨域的方案
其实解决跨域的方式一共有7种,我这里一一列举出来
- jsonp
- cors
- 基于proxy(webpack-dev-server实现)
- postmessage
- socket.io
- iframe解决
- nginx反向代理
我们前端经常使用的其实前三种,jsonp其实相对于后两者有劣势。在实际开发种最最常用的还是cors和proxy。
但是本文介绍以下前两种(面试点)
2.1 jsonp
jsonp的原理其实有一点套娃的意思。使用回调函数"骗过"浏览器。
我们可以通过使用html的script标记来进行跨域请求,并在相应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。这种跨域的通讯方式成为JSONP。
因为script的src属性本身就是一个不会产生跨域的get请求。所以我们可以将回调函数写在URL中,然后再服务端处理传值,再再客户端设置一个全局的函数来执行回调函数。
下面我们来实现一个简单的jsonp案例:
客户端server.js:
const http = require('http');
const urly = require('url');
var obj={
name:'jam',
age:'12'
}
http.createServer((req,res) => {
var parmer=urly.parse(req.url,true)
console.log(parmer);
//判断是否跨域(是否有parmer.query.callback这个值,如果有,拼接成带有参数的函数,传给客户端)
if (parmer.query.callback) {
var str=parmer.query.callback+'('+JSON.stringify(obj)+')'
res.write(str);
}else{
res.write(JSON.stringify(obj));
}
res.end();
}).listen(8000,function () {
console.log('服务器开启');
});
复制代码
客户端client.js:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script>
//要执行的回调韩素华
function hello(data) {
console.log(data);
}
</script>
//使用script标签进行get请求,后接要传给服务端的回调函数
<script src="http://127.0.0.1:8000/?callback=hello"></script>
<body>
</body>
</html>
复制代码
我们经常使用的jQuery中进行Ajax请求时设置的dataType属性为jsonp其实也是使用的这个原理。
不足之处:
- 只能进行get请求,标签限定
- 需要定义全局函数
2.2 CORS
跨域资源共享
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信 没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附 加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
这种方式也是我们经常使用的,通常在后端进行设定CORS相关属性。前端直接请求即可。
下面我贴出在express框架中的使用方法。
手动:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Authorization,X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method' )
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PATCH, PUT, DELETE')
res.header('Allow', 'GET, POST, PATCH, OPTIONS, PUT, DELETE')
next();
});
复制代码
使用cors包(封装):
npm install cors --save-dev
const cors = require('cors');
app.use(cors());
复制代码
我们这里需要注意的是在设置Origin字段时可以有两种情况:*和一个域名
在使用*(接受所有域名)的时候浏览器为了安全考虑是不会发送Cookie值的,使用一个域名的时候是可以的。但是在实际开发中我们通常只需要使用一个域名就可以完成业务了。
2.3 CORS和jsonp的比较
- jsonp可以支持老式浏览器,但是处理get请求。
- CORS支持几乎所有方式,当时不支持IE10一下浏览器
- 综合下来CORS是要比jsonp强大的。
好了关于后面的方式proxy也是比较常见的,剩下的就用的不太多了。
关于proxy后面再去整理