乐趣区

关于cors:一篇搞定面试中的跨域问题

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

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

在同源策略下,浏览器限度了跨域拜访,CORS 容许服务器指定哪些源能够拜访其资源。

同源策略 (Same-origin policy)

同源策略在 web 利用平安模型中是一个重要的概念。在这个策略下,浏览器容许第一个网页中蕴含的脚本能够获取第二个网页的数据,前提是这两个网页在同一个源下。

同源:须要 URI、主机名、端口都雷同。

这个策略能够避免一个网页上的歹意脚本通过 DOM 获取其余网页的敏感数据。

须要牢记的一点就是同源策略只利用于脚本,这意味着像 images、css 和其余动静加载的脚本 能够通过对应的标签跨域拜访资源。

是否同源的规定

同源须要满足雷同的协定(scheme),雷同的主机名(host),雷同的端口号(port)

Compared URL Outcome Reason
http://www.example.com/dir/page2.html Success Same scheme, host and port
http://www.example.com/dir2/other.html Success Same scheme, host and port
http://username:password@www.example.com/dir2/other.html Success Same scheme, host and port
http://www.example.com:81/dir/other.html Failure Same scheme and host but different port
https://www.example.com/dir/other.html Failure Different scheme
http://en.example.com/dir/other.html Failure Different host
http://example.com/dir/other.html Failure Different host (exact match required)
http://v2.www.example.com/dir/other.html Failure Different host (exact match required)
http://www.example.com:80/dir/other.html Depends 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)

退出移动版