跨域(二)CORS

CORS是一种W3C标准,用于解决跨域访问问题。它涉及浏览器和服务器之间的交互,分为简单请求和非简单请求。简单请求直接发送,非简单请求需预检OPTIONS请求。关键在于服务器设置的Access-Control-*响应头,如Access-Control-Allow-Origin、Access-Control-Allow-Credentials等。正确配置CORS能确保安全的跨域数据交换。

CORS跨域是目前使用最多的跨域解决方案,是W3C的一种技术规范,2014年起成为行业的标准,当前所有的浏览器都支持CORS。属于HTTP的一部分。

CORS(Cross-origin resource sharing):跨域资源共享。要实现CORS需要前后端同时的支持,而浏览器自动给支持CORS,所以设置主要是服务器端。

浏览器将CORS请求分为两种:简单请求和非简单请求。
简单请求(同时满足两大条件):
(1)请求方法是以下三种之一:
HEAD
GET
POST
(2)Http的头信息不超出以下几种字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

除了上述情况,其他的都属于非简单请求。两个条件是为了兼容表单(form),表单一直可以发出跨域请求,ajax只要表单可以发,它就可以直接发,表单不能发的,通过预检判断是否可以发送。

在这里插入图片描述
对于简单请求:浏览器直接将请求发送到服务端,服务端返回响应。浏览器会从响应中判断是否可以跨域,如果可以跨域,则进行正常处理,如果不能跨域,浏览器报错。

对于非简单请求:浏览器会先发送一个预检请求(preflight),在Http中就是OPTIONS请求方式。预检请求不包含主体(没有请求参数和请求体),将一些凭证、授权相关的辅助信息放在请求头中,询问服务器所在网页是否在服务器的许可名单内,以及可以使用那些Http方法和头信息字段。如果不能跨域,则实际请求不会发送并报错,如果可以跨域则发送后续请求,进行处理。

CORS相关头:

浏览器如果发送跨域请求,在http请求中就会携带一个名为Origin的头。
在这里插入图片描述
Host:目标地址
Origin:来自那里 协议+端口+域名 即判断是否同源的三要素
Referer:来自那里 协议+端口+域名+路径+参数

Referer比Origin多了路径和参数,功能类似。Orgin用于CORS请求,而Referer则被用来进行统计分析、日志记录和缓存优化等。

Origin只有跨域请求或者同于POST请求才会带上,其他情况没有Origin头,而Referer只要浏览器能获取都会携带。

浏览器获取不到请求源页面地址,Referer不会发送,但是Origin为null。

浏览器在发送简单跨域请求会带上Origin,如果发送非简单跨域请求,还会添加两个特殊字段。

Access-Control-Request-Method(必须):列出浏览器的请求会用到那些方法
Access-Control-Request-Headers(可选):指定浏览器CORS请求会额外发送的头信息字段,当使用了简单请求中5个字段之外的字段,会在OPTIONS请求头域中,执行Access-Control-Request-Headers取值。

浏览器在接受到服务器返回响应后,会从中提取相应的CORS字段进行判断。这些字段实在服务器中设置的,如果有则发回设置值,如果没有就不发。

如果是简单请求, 可能包含:
Access-Control-Allow-Origin(必选)
Access-Control-Allow-Credentials(可选)
Access-Control-Expose-Headers(可选)

如果是非简单请求,可能包含:
Access-Control-Allow-Origin(必选)
Access-Control-Allow-Credentials(可选)
Access-Control-Expose-Headers(可选)
Access-Control-Max-Age(可选)
Access-Control-Allow-Method(必选)
Access-Control-Allow-Headers(可选)

非简单请求的响应多了Access-Control-Allow-Method和Access-Control-Allow-Headers,是对非简单请求的头字段Access-Control-Request-Method和Access-Control-Request-Headers的回复。Access-Control-Max-Age表示预检请求的有效时间,在时间段内不需要再次预检请求。

Access-Control-Allow-Origin:允许的域名,只能填 *(允许任意域名)或者单域名

Access-Control-Allow-Credentials:表示是否允许发送Cookie,只有一个可选值:true(必为小写)。如果不包含cookies,请略去该项,而不是填写false。这一项与 XmlHttpRequest 对象当中的 withCredentials 属性应保持一致,即 withCredentials 为true时该项也为true;withCredentials 为false时,省略该项不写。二者必须同时设置才会起作用

当启用Access-Control-Allow-Credentials时,Access-Control-Allow-Origin不能为*。因为假设用户登录网站A后进入网站B,网站B拿着网站A的cookie登录网站A,如果网站A设置的Access-Control-Allow-Origin为任意域名,那么此时登录成功,用户数据泄露。CSRF

Access-Control-Expose-Headers:CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

Access-Control-Max-Age:用来指定本次预检请求的有效期,单位为秒。例如,Access-Control-Max-Age被设置为1728000,表示有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求

Access-Control-Allow-Method:允许跨域请求的 http 方法(如POST、GET、OPTIONS、PUT、DELETE),该字段是对于预检请求中的Access-Control-Request-Method的回复。

Access-Control-Allow-Headers:该字段指定了跨域允许设置的非简单Http请求头(5个简单Http请求头之外的头域),(当预请求中包含 Access-Control-Request-Headers 时必须包含)– 这是对预请求当中 Access-Control-Request-Headers 的回复,和上面一样是以逗号分隔的列表,可以返回所有支持的头部。

普通请求的,是浏览器先将请求发出(请求头有Origin),服务器响应后发回浏览器,浏览器从响应头中提取上述字段(Access-Control-Allow-Origin等),如果返回的Origin为 * 或者与当前同域,则表示允许跨域。如果Origin不是任意或者与当前不同域,浏览器报错,响应不给前端展示。当服务器不设置Access-Control-Allow-Origin,响应头不会包含Origin,浏览器报错并且不展示响应。

当XmlHttpRequest中设置了withCredentials为true,如果服务器响应里没有Access-Control-Allow-Credentials字段,则浏览器会报错。

如果在服务器响应中,没有携带Access-Control-Expose-Headers或者Access-Control-Expose-Headers的值不包含所期望的值,则浏览器会报错。

Access-Control-Allow-Methods:允许跨域请求的 http 方法(如POST、GET、OPTIONS、PUT、DELETE),如果响应中不包含跨域所需要的方法,浏览器报错。

Access-Control-Allow-Headers:该字段指定了跨域允许设置的非简单Http请求头(5个简单Http请求头之外的头域),(当预请求中包含 Access-Control-Request-Headers 时必须包含)– 这是对预请求当中 Access-Control-Request-Headers 的回复,和上面一样是以逗号分隔的列表,可以返回所有支持的头部。

Access-Control-Allow-Headers的值自动包含Access-Control-Expose-Headers。

### 前端资源共享(CORS)问题及其解决方案 #### 什么是? 当浏览器尝试加载来自不同名、协议或端口的资源时,就会触发问题。这种行为受到同源策略(Same-Origin Policy)的约束[^1]。 #### 资源共享(CORSCORS 是一种基于 HTTP 协议的标准机制,允许服务器明确指定哪些外部来源可以访问其资源。它通过一系列特定的 HTTP 请求头来控制客户端与服务端之间的交互[^2]。 #### 如何在前端处理 CORS 配置? ##### 1. 使用简单请求 对于简单的 GET 或 POST 请求,只要满足以下条件之一即可正常运行: - 不包含自定义头部字段; - 数据格式为 `text/plain`、`application/x-www-form-urlencoded` 或者 `multipart/form-data`。 在这种情况下,浏览器不会发送预检请求(preflight request),而是直接发起实际请求[^3]。 ```javascript fetch('https://example.com/api/data', { method: 'GET', }) .then(response => response.json()) .catch(error => console.error('Error:', error)); ``` ##### 2. 处理复杂请求 (Preflight Requests) 如果请求不符合上述简单请求的要求,则会先发出一次 OPTIONS 方法的预检请求给目标 URL 来确认是否有权限执行真正的操作前的操作。此时需要注意的是,在服务端需设置好响应这些预检请求的相关参数^。 例如,启用所有方法和任意来源的支持: ```http Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Allow-Origin: * ``` ##### 3. JSONP 实现方式 尽管 JSONP 只能用于 GET 类型的数据获取场景,并且存在安全隐患等问题,但在某些不支持现代 API 的老旧系统上仍然可能被采用作为替代方案. 注意:由于 JSONP 并不属于正式标准的一部分,因此并不推荐广泛应用于新项目当中。 ##### 4. 利用 Express 设置反向代理 另一种有效的方法是在自己的后端搭建一个中间层来进行数据转发从而规避掉直接面对第三方接口带来的限制情况。比如利用 Node.js 中非常流行的框架 express 创建这样的路由逻辑: ```javascript const express = require('express'); const app = express(); app.use('/api', function(req, res){ const proxyReq = https.request({ hostname: 'thirdparty.example.com', path: '/real-api-endpoint' + req.url, headers: {...req.headers} }, function(proxyRes){ Object.keys(proxyRes.headers).forEach(key=>res.setHeader(key,proxyRes.headers[key])); proxyRes.pipe(res); }); req.pipe(proxyReq); }); // Start server on port 8080 app.listen(8080); ``` 以上就是几种主要针对前端开发人员遇到的难题所提供的解答思路和技术手段介绍。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值