浏览器跨域问题

同源策略

浏览器同源策略:同源策略(Same origin policy)是一种约定,它是浏览器最核心也是最基本的安全功能。出于安全考虑,浏览器限制从JS脚本发起的跨源HTTP请求。
同源策略中的"源"是三个元素的组合:模式(协议),主机名(域/子域)和端口。

域名、协议、端口有一个不同就不是同源,三者均相同,这两个网站才是同源

注1:同源策略只针对于浏览器端,浏览器一旦检测到请求的结果的域名不一致后,会堵塞请求结果。这里注意,跨域请求是可以发去的,但是请求响应response被浏览器堵塞了。
注2:同源策略的非绝对性:<script> <img> <iframe> <link> <video> <audio>等带有src属性的标签可以从不同的域加载和执行资源(即不受同源策略影响)。

跨域问题的产生

跨域问题的产生:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都会出现跨域的问题。

跨域问题的解决方法

Jsonp

JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
这里就不赘述了,有时间就整理一下

CROS

CORS(Cross-Origin Resource Sharing)跨域资源共享,是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源(允许跨域访问)

注:整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CROS的请求

CORS分为两种请求,一种是简单请求,另一种是非简单请求。

为什么要分为简单请求和非简单请求?

因为浏览器对这两种请求方式的处理方式是不同的。

1. 简单请求

  • 请求方式为HEADPOST 或者 GET
  • http头信息不超出一下字段:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type(限于三个值:application/x-www-form-urlencodedmultipart/form-datatext/plain)
    只要满足以上两个条件的请求就是简单请求;

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

origin:"http://localhost:8080"

Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

浏览器会根据此次请求的响应头中是否包含Access-Control-Allow-Origin来判断跨域请求是否成功;包含则成功;否则反之;
Access-Control-Allow-Origin的值代表着允许访问该资源的"源";

此外,跨域成功的响应头中可能会包含其他字段:
如:
Access-Control-Allow-Credentials :布尔值, 表明服务器是否允许发送Cookie;默认情况下,Cookie不包括在CORS请求之中;该字段设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
另一方面,开发者必须在AjaxAxios请求中打开withCredentials属性。

axios.defaults.withCredentials = true;

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

注:如果要发送CookieAccess-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。

2.非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

预检请求

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。

浏览器发现请求是一个非简单请求时,就自动发出一个"预检"请求,要求服务器确认是否可以这样请求。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。

  • Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。
  • Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
预检请求的响应

服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-MethodAccess-Control-Request-Headers字段以后,会有两种结果:

  • 如果确认允许跨源请求,就可以做出一个包含Access-Control-Allow-Origin和其他字段的响应;
  • 如果不允许跨源请求,会做出一个没有任何CORS相关的头信息字段的响应;

服务器回应的CORS相关字段如下:

'Access-Control-Allow-Origin: '
// 跨域成功请求的响应头必须字段;
// 值为指定源 或者 *;*是指所有源
'Access-Control-Request-Method:'
// 返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
// 值为POST,GET......等
// 注:当请求是"预检"请求时,该字段是必须的
'Access-Control-Request-Headers:' 
// 如果浏览器请求包括Access-Control-Request-Headers字段,
// 则Access-Control-Allow-Headers字段是必需的。
// 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
'Access-Control-Allow-Credentials:'
//  该字段与简单请求时的含义相同。
// 布尔值, 表明服务器是否允许发送Cookie;
// 默认情况下,Cookie不包括在CORS请求之中;
// 该字段设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
'Access-Control-Max-Age:1728000'
 // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
// 有效期是1728000秒,即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

使用服务器代理

服务器代理的原理大概是这样:
代理服务器和访问源(请求端)是同源的,但和被访问服务器(资源端)是不同源的,但服务器之间的访问不受浏览器同源策略的影响(即不必担心是否有跨域问题),那么我们即可请求到同源服务器上的从被访问服务器上的获取到的请求资源了

例:例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。

目前我所知道的两种服务器代理方法:

1. Nginx反向代理

2. Node服务器

在我所参与的Vue项目中,解决跨域问题的方式就是使用代理的方式;
开发环境下使用Node做代理;通过Webpack的配置即可实现;
生产环境下使用Nginx做代理;重写Nginx配置文件即可实现;

参考文献:
https://www.jianshu.com/p/a0dd1e712c3a
https://blog.csdn.net/java_green_hand0909/article/details/78740765
https://blog.csdn.net/lambert310/article/details/51683775
https://blog.csdn.net/qq_38128179/article/details/84956552
https://blog.csdn.net/qq_31617637/article/details/72955239

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容