同源策略
同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。 同源: 协议、域名、端口号 必须完全相同。 违背同源策略就是跨域。
跨域
跨域浏览器的同源策略限制我们只能在协议、IP地址、端口号相同的情况下相互的获取数据。如果有任何一个不通,这就算是跨域 HTTP 请求,可以正常发起,但是返回的结果会被浏览器拦截。
解决跨域
JSONP
JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。
JSONP是使用 script、img、iframe,没有同源限制的标签,向服务端发送请求。返回的数据作为一个指定的回调函数的参数,在另一个 script 中指定这个回调函数,这样就可以获取到服务端数据了。
原理
在网页有一些标签天生具有跨域能力,比如:img、link、iframe、script。
JSONP就是利用script标签的跨域能力来发送请求的。
JSONP的使用
1、动态的创建一个 script 标签。
var script = document.createElement("script");
2、设置 script 的 src,设置回调函数。
script.src = "http://localhost:8080/testAJAX?callback=abc";
function abc(data) {
alert(data.name);
};
3、将 script 添加到 body 中。
document.body.appendChild(script);
4、服务器中路由的处理。
router.get("/testAJAX" , function (req , res) {
console.log("收到请求");
var callback = req.query.callback;
var obj = {
name:"孙悟空",
age:18
}
res.send(callback+"("+JSON.stringify(obj)+")");
});
CORS
官方文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS 。
CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方 案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
工作原理
CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
它由一系列传输的 HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。我们可以利用 CORS 在保证安全性的前提下访问跨域资源。
对于跨域资源的请求,浏览器已经把我们的请求发放给了服务器,浏览器也接受到了服务器的响应,只是浏览器根据同源策略,把消息给拦截了,不给我们显示。所以我们如果我们在服务器的响应中告诉浏览器这个数据是每个源都可以获取的就可以了。这就是CORS跨域资源共享。
浏览器
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。前端代码与同源完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
Web 程序发出跨域请求后,浏览器会自动向我们的 HTTP header 添加一个额外的请求头字段:Origin
,标记了请求的站点来源。如:一个由 https://www.mywebsite.com
向 https://api.website.com/users
发送的跨域 get 请求。
GET https://api.website.com/users HTTP/1/1
Origin: https://www.mywebsite.com // <- 浏览器自己加的
服务端
我们可以通过在服务器端的 HTTP 响应中添加额外的响应头字段 Access-Control-*
来表明是否允许跨域请求。根据这些 CORS 响应头字段,浏览器可以允许一些被同源策略限制的跨源响应。虽然有好几个 CORS 响应头字段,但有一个字段是必加的,那就是 Access-Control-Allow-Origin
。
如果我们有服务器的开发权限,可以将发送请求的域添加到这个响应头字段中。
Access-Control-Allow-Origin: <origin> | *
其中,origin 参数的值指定了允许访问该资源的外域 URI,也就是发送请求的那个 URI。
对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符(*),表示允许来自所有域的请求。
收到服务器返回的 response 后,浏览器中的 CORS 机制会检查 Access-Control-Allow-Origin
的值是否等于 request 中 Origin
的值。一样的话,前端成功接收到跨域资源;否则,浏览器 CORS 机制就阻止了这个响应,我们无法在我们的代码中获取响应数据。
CORS 的简单使用
主要是服务器端的设置:
router.get("/testAJAX" , function (req , res) {
//通过 res 来设置响应头,来允许跨域请求
//res.set("Access-Control-Allow-Origin","http://127.0.0.1:3000"); (允许单个地址访问)
res.set("Access-Control-Allow-Origin","*"); //允许所有地址访问
res.send("testAJAX 返回的响应");
});
简单请求
CORS 有两种类型的请求:一种是简单请求(simple request),一种是预检请求(preflight request)。
简单请求需要满足一些条件:使用的方法是 GET、HEAD 或 POST,且没有自定义 Header 字段时,一般是简单请求。
其他请求,比如使用了 PUT、DELETE 方法,或者 Content-Type
字段的类型是 application/json
,就是非简单请求,将会产生预检。具体可以看 MDN。
发送简单请求后,只要使用 Origin
和 Access-Control-Allow-Origin
对应,浏览器会直接将实际请求发送到服务器,然后服务器返回我们需要的资源。
复杂请求
与简单请求不同,”需预检的请求”会在正式通信之前,增加一次 OPTIONS
查询请求(预检请求),以获知服务器是否允许该实际请求。
假如发送一个 PUT 请求:
实际请求(actual request)
PUT http://api.website.com/user/1 HTTP/1.1
Origin: http://www.mywebsite.com
Content-Type:application/json
该请求使用 PUT 方法,并且 Content-Type
为 application/json
。所以是复杂请求,会有预检。在发送实际请求之前,客户端先使用 OPTIONS
方法发起一个预检请求,同时携带了下面的两个首部字段,包含了我们将要处理的实际请求的有关信息(服务器据此决定,该实际请求是否被允许):
预检请求(preflighted request)
OPTIONS https://api.mywebsite.com/user/1 HTTP/1.1
Origin: https://www.mywebsite.com
Access-Control-Request-Method: PUT // 告知服务器,实际请求将使用 PUT 方法
Access-Control-Request-Headers: Content-Type // 告知服务器,实际请求将携带的自定义请求首部字段:Content-Type。
服务器接收到预检请求后,会返回一个没有 body 的 HTTP 响应:
预检响应(preflighted response)
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.mywebsite.com
Access-Control-Allow-Methods: GET, POST, PUT // 表明服务器允许客户端使用POST,GET和PUT方法发起请求
Access-Control-Allow-Headers: Content-Type // 表明服务器允许请求中携带字段Content-Type
Access-Control-Max-Age: 86400 // 表明该响应的有效时间为 86400 秒(即24小时)
Access-Control-Max-Age
头字段是缓存预检响应的时间,在有效时间内,浏览器无须为同一请求再次发起预检请求,可以使用缓存来代替发送新的预检请求,减少了网络往返次数。
如果预检响应检测通过,浏览器会将实际请求发送到服务器,然后服务器返回我们需要的资源;否则,CORS 会阻止跨域访问,实际的请求永远不会被发送。
预检请求可以避免跨域请求对服务器的资源产生未预期的影响。(浏览器无法预测和确保一些请求的安全性,所以先向服务器发送一个预检请求协商一下。)
附带凭证的请求
默认情况下,跨域请求不发送身份凭证。
如果要在跨域请求中包含 cookie 和其他授权信息,需要修改两个地方:
1.将 XMLHttpRequest
的 withCredentials 属性设置为 true,向服务器发送 Cookies。2.服务器端的响应中需要携带 Access-Control-Allow-Credentials: true
,浏览器才会把响应内容返回给请求的发送者。注意:如果要发送 Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。
优缺点
优点:可以访问多种请求方式,处理机制完善,符合http规范,对于复杂请求,多一次验证,安全性更好。
缺点:不支持IE10以下浏览器。
评论区