跨域研究笔记

跨域

  • 什么是跨域?
  • 为什么要解决跨域?
  • 怎么解决跨域?

什么是跨域?

img

同源策略

出于浏览器的同源策略限制。同源策略(SameOriginPolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。

所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

下表给出了相对http://store.company.com/dir/page.html同源检测的示例:

URL结果原因
http://store.company.com/dir2/other.html成功只有路径不同
http://store.company.com/dir/inner/another.html成功只有路径不同
https://store.company.com/secure.html失败不同协议 ( https和http )
http://store.company.com:81/dir/etc.html失败不同端口 ( http:// 80是默认的)
http://news.company.com/dir/other.html失败不同域名 ( news和store )

localhost和127.0.0.1虽然都指向本机,但也属于跨域(host不同)


非同源限制
  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  • 无法接触非同源网页的 DOM
  • 无法向非同源地址发送 AJAX 请求如何解决跨域?
没有同源策略限制的两大危险场景

浏览器是从两个方面去做这个同源策略的,一是针对接口的请求,二是针对Dom的查询。

危险情景对应上述非同源限制中列举的前2点。

为什么要解决跨域?

所以同源策略是为了保护用户的信息安全而设立的。

但同时这又导致了一些正常的请求被拦截了,所以我们需要跨域 _

如何解决跨域?

CORS

跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequestFetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。

服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

CORS请求失败会产生错误,但是为了安全,在JavaScript代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

两种请求

某些请求不会触发 CORS 预检请求。我们称之为“简单请求”,若请求满足所有下述条件,则该请求可视为“简单请求”:

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的:

  1. 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
  2. 对于非简单请求,浏览器会自动发送预检请求。
预检请求

与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

对于nginx,如果请求方式是PUT,DELETE,COPY,MOVE,MKCOL等webdav标准的请求方式,需要nginx在编译时安装http_dav_module,相关命令为--with-http_dav_module

预检请求发送的header内容有

Access-Control-Request-Headers: xiaotongli # 真正请求会携带的header
Access-Control-Request-Method: POST # 真正请求所用的请求方式
Origin: http://www.a.com # 真正请求的源域

如果Access-Control-Allow-Origin 的值设置为非星号,如: http://foo.example(注意,这里的协议、域名都是需要的,端口不写,则请求头的orgin也不能写,即使是80端口也不行)。

解决案例

解决跨域的根本方式:在http的response header里添加允许跨域的信息,所以可以在web服务器端(nginx,Apache等)做、也可以在服务器语言(php,go,node等)里加,或者客户端收取http response之前(如CORS插件)

Nginx端

对应前后端分离,各自有相应域名的情况下,在前端域名下配置一个location块,让路由规则相符的请求走相应配置,将请求的内容封装好后统一发给真正的后端域名,如下:

location /api {
	proxy_pass  https://okr-api-ready.123u.com/;
}

也可以调用端(前端)域名nginx不作处理,服务器端配置如下:

	set $cors_origin '';
	if ($http_origin = 'http://www.a.com') {
		set $cors_origin $http_origin;
	}
	if ($http_origin = 'http://www.b.com') {
		set $cors_origin $http_origin;
	}
	add_header 'Access-Control-Allow-Origin' $cors_origin;
	add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
	add_header 'Access-Control-Allow-Headers' xxxt; # 自定义的header
	# $http_access_control_request_headers 可以获取全部的request headers
	add_header 'Access-Control-Allow-Credentials' true;
	add_header 'Access-Control-Max-Age' 86400; # 单位:秒
	add_header 'Access-Control-Expose-Headers' dog;

	if ($request_method = OPTIONS){
		return 200;
	}
  • Access-Control-Allow-Origin可以设置为*,具体的url(协议+域名+端口),$http_origin(这是一个nginx变量,动态获取origin)

  • 如果想设置多个域名跨域,但 Access-Control-Allow-Origin不支持设置多个域名,就算写了也不生效。可以对$http_origin做判断,如上面配置所示

  • Access-Control-Allow-Methods不支持*;不写默认只支持GET、POST、HEAD;

  • 附带身份凭证的请求与通配符,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段为true。另一方面,开发者必须在AJAX请求中打开withCredentials属性。

	var xhr = new XMLHttpRequest();
	xhr.withCredentials = true;

	// jquery中使用
	$.ajaxSetup({crossDomain: true, xhrFields: {withCredentials: true}});

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials

xhr.withCredentials = false;

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。 同时,Cookie依然遵循同源政策,只有用服务器域名(一级域名相同即可)设置的Cookie才会上传,其他域名的Cookie并不会上传。

  • CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

  • 首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

proxy_pass的配置细节

对于请求:http://www.a.com/server/product/12?mx=12

location /server/ {
    proxy_pass 'https://m.mm.top'; # 1
}
location /server {
    proxy_pass 'https://m.mm.top'; # 2
}
location /server/ {
    proxy_pass 'https://m.mm.top/'; # 3
}
location /server {
    proxy_pass 'https://m.mm.top/'; # 4
}

以上4种配置效果说明:

http://www.a.com称为原域名,https://m.mm.top称为新域名

  • 1,2情况下,proxy_pass的域名后不带斜杠/,最终效果是新域名(无斜杠)替换了原域名(无斜杠),故:
    • 1的最终地址是https://m.mm.top/server/product/12?mx=12
    • 2的最终地址是https://m.mm.top/server/product/12?mx=12
    • 1,2的最终地址一致,即proxy_pass的域名后无斜杠,则location后的表达式是否有斜杠不影响最终地址
  • 3,4情况下,proxy_pass的域名后带斜杠/,最终效果是新域名(有斜杠)替换了原域名(无斜杠)+ location的表达式(以斜杠开头),故:
    • 3的最终地址是https://m.mm.top/product/12?mx=12
    • 4的最终地址是https://m.mm.top//product/12?mx=12
    • 注意4的最终地址多了一个斜杠,故proxy_pass的域名后有斜杠,location的表达式最后是否有斜杠效果是不同的
PHP端修改header
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
	header('HTTP/1.1 200');
}
if (isset($_SERVER["HTTP_ORIGIN"])) {
	header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);
}
header('Access-Control-Allow-Methods:GET,POST,PUT,DELETE,OPTIONS,PATCH');
header('Access-Control-Allow-Headers:Xiaotongli');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 3');
header('Access-Control-Expose-Headers:pig,dog');
  • Access-Control-Allow-Origin这个header头,如果nginx和php同时设置的话,预检请求会报设置了多个origin,导致失败。
  • 经测试,对于预检请求,在php里设置OPTIONS返回的状态码,2开头的都可以通过,nginx里设置的返回码,只有200到204可以预检通过。

注:

  • 前端手动设置origin来模拟会被浏览器拒绝

参考:

  • https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
  • http://www.ruanyifeng.com/blog/2016/04/cors.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值