头图

什么是CORS(跨源资源共享)?

CORS(Cross-Origin Resource Sharing)是一种机制,允许网页从不同的域访问服务器上的资源。

在同源策略下,浏览器限制了跨域访问,CORS允许服务器指定哪些源可以访问其资源。

同源策略(Same-origin policy)

同源策略在web应用安全模型中是一个重要的概念。在这个策略下,浏览器允许第一个网页中包含的脚本可以获取第二个网页的数据,前提是这两个网页在同一个源下。

同源:需要URI、主机名、端口都相同。

这个策略可以防止一个网页上的恶意脚本通过DOM获取其他网页的敏感数据。

需要牢记的一点就是同源策略只应用于脚本,这意味着像images、css和其他动态加载的脚本 可以通过对应的标签跨域访问资源。

是否同源的规则

同源需要满足相同的协议(scheme),相同的主机名(host),相同的端口号(port)

Compared URLOutcome Reason
http://www.example.com/dir/page2.htmlSuccess Same scheme, host and port
http://www.example.com/dir2/other.htmlSuccess Same scheme, host and port
http://username:password@www.example.com/dir2/other.htmlSuccess Same scheme, host and port
http://www.example.com:81/dir/other.htmlFailure Same scheme and host but different port
https://www.example.com/dir/other.htmlFailure Different scheme
http://en.example.com/dir/other.htmlFailure Different host
http://example.com/dir/other.htmlFailure Different host (exact match required)
http://v2.www.example.com/dir/other.htmlFailure Different host (exact match required)
http://www.example.com:80/dir/other.htmlDepends Port explicit. Depends on implementation in browser.

::: tip
scheme: http https
:::

CORS存在的意义

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。

出于安全性,浏览器限制脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。

跨源域资源共享(CORS)机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行。

CORS的工作原理是什么?

当浏览器发起跨域请求时,会在请求头中添加Origin字段,指示请求的源。服务器接收到请求后,会在响应头中添加Access-Control-Allow-Origin字段,指示允许访问的源。如果服务器允许该源访问资源,浏览器会将响应返回给客户端。

CORS中的预检请求是什么?为什么需要预检请求?

预检请求是浏览器在发送跨域请求前发送的一种OPTIONS请求,用于检查服务器是否支持跨域请求。预检请求中包含一些额外的头信息,如Access-Control-Request-Method、Access-Control-Request-Headers等。

预检请求的目的是确保服务器支持跨域请求,避免跨域请求对服务器造成安全风险。只有在服务器返回允许的响应头后,浏览器才会发送实际的跨域请求。

了解了上面的内容,我们解决浏览器控制台的跨域问题,一般有两个方向:

  1. 后端服务设置允许跨域访问
  2. 前端通过代理访问资源(开发阶段使用)

如何在服务器端配置CORS?

在服务器端配置CORS通常需要在响应头中添加一些字段,如Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers等。通过配置这些字段,可以控制允许的源、请求方法和请求头。

用 Node.js 的一个框架 koa 来举例,解决跨域使用 koa-cors 非常简单,如下:

var koa = require('koa');
var route = require('koa-route');
var cors = require('koa-cors');
var app = koa();

app.use(cors());

app.use(route.get('/', function() {
  this.body = { msg: 'Hello World!' };
}));

app.listen(3000);

这个中间件大概做了这样的事情:

module.exports = () => {
  return async function(ctx, next) {
    ctx.set('Content-Type', 'application/json');
    ctx.set('Access-Control-Allow-Origin', '*');
    ctx.set('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, PUT');
    ctx.set('Access-Control-Allow-Headers', 'X-Requested-With, content-type, X-Authorization, X-uuid');
    ctx.json = json.bind(ctx);
    ctx.halt = halt.bind(ctx);
    try {
        await next();
    } catch (e) {
        return ctx.halt(e.code, e.message);
    }
  };
};

这样前端收到的响应会是下面的样子:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

本例中,服务端返回的 Access-Control-Allow-Origin: * 表明,该资源可以被 任意 外域访问。

前端代理

如果使用了webpack 那么配置一个代理就很容易,通过代理模拟出和服务端同源的请求。

//webpack.config.js
module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        pathRewrite: { '^/api': '' },
      },
    },
  },
};

这样前端发起的请求包含/api的路径就会被代理到 http://localhost:3000,并且会把/api替换为空。如果你的接口地址本来就包括/api,那只要把pathRewrite: { '^/api': '' }去掉即可。

这个devServer代理使用功能强大的 http-proxy-middleware 软件包。

http-proxy-middleware 内部又使用了 node-http-proxy

服务端代理

服务端代理是一种解决跨域访问的方法,通过在服务端发起请求并将结果返回给客户端,绕过了浏览器的同源策略限制。通常可以使用Node.js、Java、Python等后端语言来实现服务端代理。

以下是一个使用Node.js实现服务端代理的示例代码:

const express = require('express');
const request = require('request');

const app = express();

app.get('/proxy', (req, res) => {
  const url = req.query.url;
  request(url, (error, response, body) => {
    if (!error && response.statusCode === 200) {
      res.send(body);
    } else {
      res.status(500).send('Error');
    }
  });
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

在上面的示例中,当客户端发送请求到/proxy接口时,服务端会将请求转发到指定的URL,并将结果返回给客户端。

总结

总的来说,跨域资源共享是一个常见的Web开发问题,了解跨域资源共享的原理和解决方法对于开发安全可靠的Web应用程序非常重要。

参考链接

跨源资源共享(CORS)

Search Engine


来了老弟
508 声望31 粉丝

纸上得来终觉浅,绝知此事要躬行