前端跨域CORS
什么是跨域
由于浏览器的同源策略,浏览器不能请求其他网站的url,所谓同源策略,指的是协议、域名、端口相同的请求。
当js脚本使用Ajax请求其他domain下的资源时,就会造成跨域问题。
浏览器预检
浏览器在发送请求时,会先进行CORS请求预检来判断请求是否跨域。预检向服务器发送一个OPTIONS
请求,官方对OPTIONS
的定义为:
HTTP的OPTIONS方法用于获取目的资源所支持的通信选项。客户端可以对特定的URL使用OPTIONS方法,也可以对整站(通过将 URL 设置为“*”)使用该方法。
选项 | 是否允许 | 备注 |
---|---|---|
Request has body | No | 没有请求体 |
Successful response has body | No | 成功的响应有响应体 |
Safe | Yes | 安全 |
Idempotent | Yes | 密等性,不变性,同一个接口请求多少次都一样 |
Cacheable | No | 不能缓存 |
Allowed in HTML forms | No | 不能在表单里使用 |
预检触发机制
当一个请求为simple request时,不会触发CORS预检请求;当一个请求为not-simple request时,则会触发CORS预检请求。
simple request:
-
请求方式是
HEAD
、GET
、POST
-
HTTP的头部信息为以下字段
- Accept
- Accept-Language
- Content-Language
- Content-Type 只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Viewport-Width
not-simple request:包含以下头部字段
字段名 | 位置 | 用法 | 备注 |
---|---|---|---|
Origin | 请求头 | origin | 表明预检请求或实际请求的源站 |
Access-Control-Request-Method | 请求头 | method | 将实际请求所使用的 HTTP 方法告诉服务器。 |
Access-Control-Request-Headers | 请求头 | field-name[, field-name]* | 将实际请求所携带的头部字段告诉服务器。 |
Access-Control-Allow-Origin | 响应头 | origin or * | 对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求 |
Access-Control-Allow-Methods | 响应头 | method[, method]* | 指明了实际请求所允许使用的 HTTP 方法。 |
Access-Control-Allow-Headers | 响应头 | field-name[, field-name]* | 指明了实际请求中允许携带的头部字段。 |
Access-Control-Allow-Credentials | 响应头 | true | 指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容 |
Access-Control-Max-Age | 响应头 | delta-seconds | 指定了请求的结果能够被缓存多久 |
JS非预检请求
fetch("http://localhost:5501/cors.html", {
method: "get",
});
可以发现请求正常进行且无其他请求,此时为非预检请求。
JS发送预检请求
fetch("http://localhost:5501/cors.html", {
method: "get",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
});
当新增请求头设置请求不为simple request时,可以看到此时浏览器多出一条预检(preflight)请求
express解决跨域问题
在服务端可以设置允许请求头来解决跨域,例如:
let allowCorsDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'POST')
res.header('Access-Control-Allow-Headers', 'content-type')
next()
}
app.use(allowCorsDomain)
设置Access-Control-Allow-Origin: *
代表所有的origin都允许跨域。
也可以调用Cros中间件解决跨域。
Cookie的跨域
默认情况下,Cookie是不包含在CORS的请求中,当需要用Cookie传输数据时,需要服务器端使用Access-Control-Allow-Credentials
字段,并且Access-Control-Allow-Origin
不能是通配符,否则会报错,可以设置具体的域或者使用中间件,最后客户端请求时设置withCredentials
属性
const cookieParser = require('cookie-parser');
app.use(cookieParser())
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'POST')
res.header('Access-Control-Allow-Headers', 'content-type, X-Custom-Header')
res.header('Access-Control-Allow-Credentials', 'true')