拜拜,防跨域

245天前 · 代码 · 384次阅读

防跨域于我而言一直是个模糊的怪兽。每次遇到的时候都只会去找百度超人帮忙,却从没尝试要自己解决问题。直到昨天,Hokori学长把我从舒适圈踢了出去,我这才第一次直面这个怪兽,解开它的真面目。

前置工作

在了解跨域问题之前,首先需要了解浏览器的同源策略。

同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

浏览器是我们接触互联网的一个主要工具之一。在面对互联网琳琅满目的信息时,我们也需要注意其中的危险,例如诈骗信息,病毒等等。而浏览器为了我们的安全着想,就整出了这么个同源策略。通过这个同源策略,我们可以轻松规避一些问题,例如

  • 防止JavaScript代码对非同源页面的各种请求(CSRF攻击)
  • 限制对其他页面DOM元素的读取(读取你的用户名密码)

跨域问题的出现

跨域问题你可能也已经猜到了,就是触发了浏览器的同源策略的干预。在采用前后端分离的时候,我们并不会将两个部分放在同一个服务器上,而且域名及端口一般也不同。而同源的定义恰好就是同域名,同端口,同协议。举个例子,与http://store.company.com/dir/page.html的源进行对比,不同URL的不同结果如下表[tip]数据来自MDN[/tip]

URL结果原因
http://store.company.com/dir2/other.html同源只有路径不同
http://store.company.com/dir/inner/another.html同源只有路径不同
https://store.company.com/secure.html非同源协议不同
http://store.company.com:81/dir/etc.html非同源端口不同
http://news.company.com/dir/other.html非同源子域不同

这样一看,进行前后端分离时,大概率会触发同源策略的干预了。那么怎么解决呢?

解决方案

现在主流的解决方案基本上都是基于CORS(跨源资源共享)。

在探讨CORS之前,要得先了解一下HTTP中的简单请求与非简单请求。简单请求包含GET, HEAD, POST,同时它们的HTTP头信息不得多于以下信息

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type(application/x-www-form-urlencodedmultipart/form-datatext/plain

不满足以上条件的则是复杂请求。

那么了解了简单请求之后,再来看看CORS。

跨源资源共享 (CORS) (或通俗地译为跨域资源共享)是一种基于HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。在预检中,浏览器发送的头中标示有HTTP方法和真实请求中会用到的头。

如MDN所言,CORS在发送真实请求之前会通过一个"预检"请求来判断是否能够继续发送。具体来说就是当发送复杂请求之前,会先发送一个 OPTIONS请求来判断。当服务器返回了允许的信号后,浏览器才会继续发送真实请求。“预检请求”的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

而简单请求则不需要进行“预检请求”。

发送预检请求时一般会带有以下头部信息

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

首部字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。首部字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHERContent-Type。服务器据此决定,该实际请求是否被允许。

而服务器的预检响应一般带有

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET OPTIONS 方法发起请求。该字段与 HTTP/1.1 Allow: response header 类似,但仅限于在需要访问控制的场景中使用。

首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER Content-Type。与Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。

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

说到这里,其实我们的解决方案也差不多出来了。

1. 通过WEB服务器配置

nginx为例,只需要配置文件中添加

location / {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
}

基本上就可以万事大吉。

2. 在后端添加响应头

PHP为例,在入口文件添加

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization');

不用CORS还有没有别的办法呢?答案是有的。

3. WEB服务器反代

同源策略仅是针对浏览器的安全策略。服务器端调用HTTP接口只是使用HTTP协议,不需要同源策略,也就不存在跨域问题。

还是以Nginx为例,通过Nginx配置一个代理服务器域名与domain1相同但端口不同来做跳板,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问。

server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;
    }
}

最后还有一招精神胜利法,那就是

4. 关闭浏览器的同源策略

这招太损了,就不贴方法上来了。

小结

摸清楚跨域问题后发现其实它真的没那么可怕,只要搞清楚原理,一切都将迎刃而解。

参考

跨域资源共享 CORS 详解 - 阮一峰的网络日志 (ruanyifeng.com)

Nginx配置跨域请求 Access-Control-Allow-Origin * - SegmentFault 思否

跨域问题产生的原因以及十种解决方案-华为开发者联盟 (huawei.com)

跨源资源共享(CORS) - HTTP | MDN (mozilla.org)

👍 14

none

最后修改于237天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

狗头

原神

小黄脸

  1. nihilism 149天前

    加油

  2. qinians 163天前

    加油。。。。

  3. Hokori 167天前

    jsonp,websocket的跨端方案也可以讲讲

    1. 季悠然 166天前

      我要hokori给我讲

      1. Hokori 166天前

        ???

  4. SomeBottle 239天前

    感觉预检请求好像是近几年才能在浏览器控制台看到的
    之前尝试搞评论框和某用图床放视频的时候也被跨域唬住了
    但确实,方法总比困难多

  5. 超越自我吧 241天前

    每次都是需要使用的时候再重新去度娘一下。

  6. GouGo 245天前

    emo了

    1. 季悠然 245天前

      很有精神!

目录

avatar

季悠然

寻找有趣的灵魂

127

文章数

1954

评论数

3

分类

好热啊

arknights!

我和你,可以做朋友吗?

107