跨域理解及解决

什么是跨域

跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!

浏览器的同源策略

是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;

跨域错误信息产生的原因

为了说明问题,我们可以做如下实验,我们在本地搭建了开发环境, 由客户端 http://127.0.0.1:81 向服务器http://127.0.0.1:82 发送两个请求,一个使用 javascript 异步请求数据,另一个使用 img 标签请求数据,服务器收到请求后,打印接收到请求的日志,如下图所示:
客户端发送两个请求,服务端打印日志并处理请求
  看客户端浏览器的控制台,可以看到发出了两个请求,并且都收到了状态码为 200 的响应,同时控制台报了一个错误,即 xhr 请求报错。由此我们可以知道,之所以产生跨域错误信息,原因有以下三条:
  • 浏览器端的限制(服务端收到了请求并正确返回)
  • 发送的是 XMLHttpRequest 请求(使用 img 标签发送的请求,并不会报错)
  • 请求了不同域的资源
只有同时满足了这三个条件,浏览器才会产生跨域错误。
解决跨域的思路
  • 不发送 XHR 请求(使用jsonp)
  • 解决跨域

服务器反向代理

http {
    upstream myapp1 {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}
 

跨域资源共享CORS

CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。CORS 基于 http 协议关于跨域方面的规定,使用时,客户端浏览器直接异步请求被调用端服务端,在响应头增加响应的字段,告诉浏览器后台允许跨域。
 
跨域错误
  回到文章开始的这个跨域错误信息,可以看到错误的具体信息是:服务端没有设置Access-Control-Allow-Origin 这个响应头从而导致报错,通过设置 Access-Control-Allow-Origin: * 这个响应头,我们可以解决问题。但是,这种设置能满足所有情况吗? 更进一步,使用 CORS 时浏览器如何检查跨域错误? 前面我们有讲到,虽然浏览器报错,但是在这之前服务端已经接受了请求,那么,浏览器总是先发出请求后再进行判断吗?下面我们一一讨论。
浏览器检查跨域错误的基本原理是:
浏览器检测到 ajax 请求的域与当前域不一致,会在请求头中增加 Origin 字段,然后检查服务端响应头 Access-Control-Allow-Origin,如果不存在或不匹配,则报跨域错误。
浏览器总是先发出请求,然后根据是否有 Access-Control-Allow-Origin 响应头来判断吗
  答案是,对于简单请求,是;而对于非简单请求,不是。非简单请求的情况下,浏览器并不是直接请求所需资源,而是会先发出一个预检请求,预检请求通过后才会对所需资源进行请求。
 
非简单请求预检请求
  MDN 对非简单请求进行了定义,满足下列条件之一,即为非简单请求:
  1. 使用了下列 HTTP 方法:PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
  2. 使用了除以下首部之外的其他首部:Accept、Accept-Language、Content-Language、Content-Type
  3. Content-Type首部的值不属于下列其中一个: application/x-www-form-urlencoded、 multipart/form-data、 text/plain
  4. 请求中的 XMLHttpRequestUpload 对象注册了任意多个事件监听器
  5. 请求中使用了ReadableStream对象
  简单来说,除了我们平时使用最多的 GET 和 POST 方法,以及最常使用的 Accept、Accept-Language、Content-Language 和 类型为 application/x-www-form-urlencoded、 multipart/form-data、 text/plain 的 Content-Type 请求头,其他基本都是非简单请求。对于这些非简单请求,浏览器会发出两个请求,第一个为 OPTIONS 遇见请求,遇见请求的响应检查通过后才会发出对资源的请求。
非简单请求过程
  生产环境下,如果需要发送非简单跨域请求,每次两个请求会增加响应时间,为此,W3C 标准中增加了另一个响应头 Access-Control-Max-Age 参数,该响应头表明了对于非简单请求的预检请求浏览器的缓存时间,在缓存有效期内,非简单请求可以不发送预检请求,另外,实际开发中,可以在服务端设置接收到的请求方法是 OPTIONS 时,直接返回 200,这样也能加快响应。
 
设置 Access-Control-Allow-Origin: * 就行吗
带cookie的跨域
  可以看到响应头中有 Set-Cookie,再次请求时,如果是同源请求,浏览器会将 Set-Cookie 中的值放在请求头中,但是对于跨域请求,默认是不发送这个 Cookie 的。 如果要让浏览器发送 cookie,需要在 Client 设置 XMLHttpRequest 的 withCredentials 属性为 true。
并且当请求中包含凭证信息时,需要在服务端设置响应头 Access-Control-Allow-Credentials
 
CORS请求头和响应头总结
请求头
 
Origin
浏览器发出 Ajax 跨域请求之前会添加此头部,值为发送请求的域
Access-Control-Request-Method
使用了除 GET、POST 请求方法之外的方法,浏览器会添加此头部,值为当前请求方法
Access-Control-Request-Headers
使用了自定义头部或除了Accept、Accept-Language、Content-Language、Content-Type 之外的头部,浏览器会添加此头部,值为当前的请求方法
相应头
 
Access-Control-Allow-Origin
表示服务端允许哪些域请求资源
Access-Control-Allow-Methods
当客户端包含 Access-Control-Request-Method 请求头时,服务端需要响应该头部,值通常由 Request 的 header 中 Access-Control-Request-Method 取得
Access-Control-Allow-Headers
当客户端包含 Access-Control-Request-Headers 请求头时,服务端需要响应该头部,值通常由 Request 的 header 中 Access-Control-Request-Headers 取得
Access-Control-Expose-Headers
指出客户端通过 XHR 对象的 getResponseHeaders 方法可以获取的响应头有哪些
Access-Control-Allow-Credentials
允许带 cookie 的跨域请求
Access-Control-Max-Age
预检请求的缓存时间

posted on 2025-07-02 18:24  天军  阅读(8)  评论(0)    收藏  举报

导航