1. 什么是跨域(同源策略)
同源策略:限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制
同源的定义:如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。我们也可以把它称为“协议/主机/端口 tuple”,或简单地叫做“tuple". (“tuple” ,“元”,是指一些事物组合在一起形成一个整体,比如(1,2)叫二元,(1,2,3)叫三元)
不同源则跨域,例如http://www.example.com/
因为浏览器的同源策略导致了跨域。
同源策略限制内容有:
- Cookie、LocalStorage、IndexedDB 等存储性内容
- DOM 节点 ,AJAX 请求不能发送
但是有三个标签是允许跨域加载资源:
1.<img src=XXX>
2.<link href=XXX>
3.<script src=XXX>
- ajax请求时,浏览器要求当前网页和server必须同源(安全)
- 同源:协议/域名/端口,三者必须一致
- 前端:http://a.com:8080/; server: https://b.com/api/xxx (默认端口80)
三者都不同源
跨域:
- 所有的跨域,都必须经过server端允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
Jsonp
JSONP
- 访问https://immoc.com/,服务端一定返回一个html文件吗?
- 服务器可以任意动态拼接数据返回,只要符合html格式要求
- 同理与
<script src=https://imooc.com/getData.js>
JSONP基本实现原理
- script可绕过跨域限制
- 服务器可以任意动态拼接数据返回
- 所以script就可以获得跨域的数据,只要服务端愿意返回
核心代码演示:
Jsonp.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p>一段文字</p>
<script>
window.callback = function(data) {
console.log(data)
}
</script>
<script src="http://localhost:8001/jsonp.js?username=xxx"></script>
</body>
</html>
Jsonp.js
callback(
{
name: "zhangsan"
}
)
效果:
Script可以利用js动态插入
jsonp 的跨域,它只支持get 方式的请求,因为它是使用script 标签去发送请求,而且服务端需要做处理,客户端也需要做处理。如果跨域的时候传递的数据非常多,jsonp 的方式就不太可取。
CORS(跨域资源共享)
前端发送请求时,服务器给一个响应头,告诉客户端,这些数据可以访问
jsonp 与 cors 的区别:
jsonp 是最早期的这种跨域解决方案,因为是使用script 标签发送请求,所以兼容性会比较好,只支持 get方式
cors 是后期的一种解决方案,它只需要改服务器的配置,客户端不需要做任何的处理(一个响应头)兼容性稍微差一些,因为Access-Control-Allow-Origin是后期http协议规定的支持get ,以及post
其它方式:通过后台服务器转发,通过设置代理服务器(nginx)
CORS(Cross-Origin Resource Sharing, 跨源资源共享)是W3C出的一个标准,其思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。因此,要想实现CORS进行跨域,需要服务器进行一些设置,同时前端也需要做一些配置和分析。
koa2服务端设置:
npm install --save koa2-cors
Koa2(服务端)中CORS配置:
目录:
cors_config.js
const cors = require('koa2-cors'); //跨域处理
var cors_config = new cors(
{
origin: function(ctx) { //设置允许来自指定域名请求
return 'http://localhost:8080' //默认允许本地请求3000端口可跨域
},
maxAge: 5, //指定本次预检请求的有效期,单位为秒。
credentials: true, //是否允许发送Cookie
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],//设置所允许http的请求方法
allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //设置服务器支持的所有头信息字段
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //设置获取其他自定义字段
});
module.exports = cors_config
app.js加载插件
目录:
app.js
const Koa = require('koa')
const app = new Koa()
const cors = require('./config/cors_config')
app.use(cors)
module.exports = app
前端的配置:
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求:
简单请求满足以下条件:
- 使用下列方法之一:
GET
HEAD
POST
2.HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Content-Type:值属于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
非简单请求:
请求方式:PUT、DELETE
自定义头部字段
发送json格式数据
正式通信之前,浏览器会先发送OPTION请求,进行预检,这一次的请求称为“预检请求”
服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据
大部分的请求是需要用户携带着用户信息的,比如在一个登录的系统中,用户会携带着相应的cookie或token,但CORS跨域默认是不带身份凭证的。如果需要附带身份凭证,在发送请求时,通过将withCredentials属性设置为true,可以指定某个请求可以发送凭据。
附带身份凭证对服务端有两个要求:
服务端的Access-Control-Allow-Origin头部不能设置为*
服务端的Access-Control-Allow-Credentials头部设置为true