通过CORS进行跨域Ajax请求

通过CORS进行跨域Ajax请求

在XMLHttpRequest中,跨域请求是不被允许的,通常会使用jsonp等方案进行跨域。但是现行标准可以利用CORS(Cross-origin resource sharing)方案进行跨域请求。但是请注意这种方案存在兼容问题,IE8不能正常使用。所以如果考虑兼容还是jsonp更好一点。

在做跨域ajax的请求时,客户端和服务器都需要进行专门的设置。


简单请求和预检请求

某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”。请求满足下列所有条件则认定为简单请求。

1.使用下列方法之一:

GET
HEAD
POST

2.除了被用户代理自动设置的首部字段(例如 Connection ,User-Agent)和在 Fetch 规范中定义为 禁用首部名称 的其他首部,允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:

Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width

3.Content-Type 的值仅限于下列三者之一:

text/plain
multipart/form-data
application/x-www-form-urlencoded

4.请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。 5.请求中没有使用 ReadableStream 对象。

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

具体可参阅MDN

服务端

跨域的允许主要由服务器端控制,服务器端通过在响应的 header 中设置 Access-Control-Allow-Origin 及相关一系列参数,提供跨域访问的允许策略。


服务端设置

Access-Control-Allow-Origin

该头选项设置了同源安全策略。可以设置为 *,但是副作用是客户端发送请求时不会携带Cookie

通常可以设置为以下选项。

Access-Control-Allow-Origin: http://127.0.0.1
Access-Control-Allow-Origin: http://127.0.0.1:8080
Access-Control-Allow-Origin: https://127.0.0.1
Access-Control-Allow-Origin: http://.example.com
Access-Control-Allow-Origin: *


Access-Control-Allow-Credentials

是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回。

通常可以设置为以下选项。

Access-Control-Allow-Credentials: true


Access-Control-Allow-Methods

在对预检请求(preflight request)的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。

通常可以设置为以下选项。

Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Methods: GET


Access-Control-Max-Age

设定预检请求(preflight request)的有效期,单位为秒。在此期间不用发出另一条预检请求。

注意:不同的浏览器缓存时间上限并不相同。当值为-1时,禁用缓存。

通常可以设置为以下选项。

Access-Control-Max-Age: 600
Access-Control-Max-Age: -1


Access-Control-Allow-Headers

用于预检请求(preflight request)中,列出了将会在正式请求的 Access-Control-Request-Headers 字段中出现的首部信息。

注意以下这些特定的首部是一直允许的:Accept, Accept-Language, Content-Language, Content-Type (但只在其值属于 MIME 类型 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种时)。这些被称作simple headers,你无需特意声明它们。

通常可以设置为以下选项。

Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests


服务端代码示例

此处使用PHP代码作为示例,对于其他语言一样通用。

<?php
/**
    这段代码启用了cros,但只允许http://127.0.0.1请求该域的资源。
    也就是说客户端当时访问的网页的域必须是http://127.0.0.1。域名端口和协议都不能变。
    设置了只允许GET, POST, PUT, DELETE, OPTIONS请求。且开启了Cookie。
    还允许了自定义HTTP头mysessid的提交。
*/    
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Origin: http://127.0.0.1");
header("Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS");
header('Access-Control-Allow-Headers:x-requested-with,content-type,mysessid');

客户端

跨域的请求必然需要客户端的配合。


客户端设置

Access-Control-Request-Headers

出现于预检请求(preflight request)中,用于通知服务器在真正的请求中会采用哪些请求头。也就是客户端请求头会有哪些。


Access-Control-Request-Methods

出现在预检请求(preflight request)用于通知服务器在真正的请求中会采用哪种 HTTP 方法。


客户端代码示例

此处使用jquery的ajax请求,原生js同理。

$.ajax({
    url: "http://127.0.0.1/",
    type: 'GET',
    crossDomain: true,
    beforeSend: function(req) {
        req.setRequestHeader("mysessid", ""); //这里传输了自定义头mysessid
    },
    xhrFields: {
        withCredentials: true // 这里设置了withCredentials,表明传输cookie
    },
    success: function(data) {
        console.log(data)
    },
    error: function(err) {
        console.error(err)
    }
})

注意事项

有几个注意事项需要注意一下。

1. 避免混乱

Access-Control-Allow-Headers 和 Access-Control-Request-Headers

前者属于服务端设置,应该在响应头里。

后者属于客户端设置,应该在请求头里。

如果出现在对方的位置,一定是错误的设置,其他头同理。

2. Cookie和自定义HTTP头的使用

Cookie的传递和自定义HTTP头收多个头相互影响,在这点要多加注意。

qrcode

创建时间:2020-07-05 12:51:00

最后修改:2020-07-05 14:17:43