CORS(Cross-Origin Resource Sharing, 跨源资源共享) 是 W3C 出的一个标准,其思想是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。因此,要想实现 CORS 进行跨域,需要服务器进行一些设置,同时前端也需要做一些配置和分析。本文简单的对服务端的配置和前端的一些设置进行分析。
本文服务端的代码采用的是 node,使用 koa,koa-router 和 koa2-cors。
配置的主要代码如下:
- app.use(cors({
- origin: function(ctx) {
- const regexp = new RegExp('/CORS');
- const regexpWith = new RegExp('/CORSWith');
- if(regexpWith.test(ctx.url)) {
- return `http://${packageData.url}:7000`;
- } else if(regexp.test(ctx.url)) {
- return '*'} else if(~String(ctx.url).indexOf('/imgs/')) {
- return `http://${packageData.url}:7000`;}
- return false;
- },
- exposeHeaders: ['WWW-Authenticate', 'Server-Authorization', 'Date'],
- maxAge: 100,
- credentials: true,
- allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
- allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Custom-Header', 'anonymous'],
- }));
主要是使用了 koa2-cors 进行了配置,下面对配置项进行简单介绍:
设置 Access-Control-Allow-Origin, 源码如下:
Access-Control-Allow-Origin 表示允许跨域的域名,可以设置为 * 也可以设置为具体的域,其中,* 表示全部,即所有的域名下的请求都允许,但设置为 * 后,所有的请求都不会携带附带身份凭证 (比如 cookie);设置为具体的域则表示只有该域下的请求允许,别的域下的请求不被允许,设置为具体的域是请求中携带身份凭证的基础。
设置 Access-Control-Expose-Headers,源码如下:
Access-Control-Expose-Headers 表示允许脚本访问的返回头,请求成功后,脚本可以在 XMLHttpRequest 中访问这些头的信息,如在上面配置的代码中设置了 Date, 在浏览器端就可以通过 js 拿到服务器的时间了, 下面是通过 XMLHttpRequest 实例的 getResponseHeader() 获取服务器时间的代码:
- let xhr = newXMLHttpRequest();
- xhr.open('GET', `http://${url}/CORS/userInfo/12`, true);
- xhr.onload = function() {
- if(xhr.readyState == 4) {
- try{
- // 获取服务器时间
- console.log(xhr.getResponseHeader('Date'));
- } catch(ex) {
- new Error(ex);
- }
- }
- };
- xhr.send();
设置 Access-Control-Max-Age,源码如下:
Access-Control-Max-Age 用来指定本次预检请求的有效期,单位为秒,预检请求将在下面具体介绍,现在先过。
设置 Access-Control-Allow-Credentials,源码如下:
Access-Control-Allow-Credentials 为服务端标识浏览器请求 CORS 时是否可以附带身份凭证,对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为 "*"。
设置 Access-Control-Allow-Methods,源码如下:
Access-Control-Allow-Methods 用来设置检查网络请求的方式,如 GET、POST 等。
设置 Access-Control-Request-Headers,源码如下:
Access-Control-Request-Headers 用来将实际请求所携带的首部字段告诉服务器,在这里可以自定义头部信息,用来对浏览器的非简单请求进行预检判断。
前端的配置主要通过简单请求和非简单请求,携带身份凭证,canvas 中画图使用的跨域图片三部分进行讲解
浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求满足以下条件:
1. 使用下列方法之一:
2.HTTP 的头信息不超出以下几种字段:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
简单请求如图所示:
浏览器与服务器之间请求只进行了一次。
不满足简单请求条件的请求则要先进行预检请求,即使用 OPTIONS 方法发起一个预检请求到服务器,已获知服务器是否允许该实际请求。
非简单请求如下所示:
下面是 PUT 请求第一次返回的结果:
通过 PUT 请求结果可以看出,当检测到 PUT 请求为非简单请求时,浏览器便会发送一个预检请求,目的是询问,自定义头部 X-Custom-Header 的 PUT 请求是否被允许,浏览器返回了所有可以请求的方法和自定义的头部(把所有可以的返回是为了避免多次预检请求),这时候预检请求成功了,便会发送真正的 PUT 请求。
关于预检请求,需要注意一下两点:
大部分的请求是需要用户携带着用户信息的,比如在一个登录的系统中,用户会携带着相应的 cookie 或 token,但 CORS 跨域默认是不带身份凭证的。
如果需要附带身份凭证,在发送请求时,通过将 withCredentials 属性设置为 true,可以指定某个请求可以发送凭据。
下面提供针对 XMLHttpRequest 附带身份凭证的兼容性写法:
- functioncreateCORSRequest(method, url) {
- var xhr = newXMLHttpRequest();
- xhr.onload = function() {
- if(xhr.readyState == 4) {
- try{
- if((xhr.status >= 200 && xhr.status < 300) || xhr == 304) {
- console.log(xhr.response);
- } else{
- console.log('Request was unsuccessful: ' +xhr.status);
- }
- } catch(ex) {
- newError(ex);
- }
- }
- };
- if('withCredentials' inxhr) {
- xhr.open(method,url, true);
- } else if(typeof XDomainRequest != 'undefined') {
- xhr = newXDomainRequest();
- xhr.open(method, url);
- } else{
- xhr = null;
- }
- return xhr;
- }
附带身份凭证对服务端有两个要求:
尽管不通过 CORS 就可以在画布中使用图片,但是这会污染画布。一旦画布被污染,你就无法读取其数据。例如,你不能再使用画布的 toBlob(), toDataURL() 或 getImageData() 方法,调用它们会抛出安全错误。
这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露。
对跨域图片进行修改的话,img 需要添加 crossOrigin 属性,代码如下:
- functiondrawCanvas(id, drawId, url) {
- let canvas =document.getElementById(id);
- let ctx = canvas.getContext('2d');
- var img = document.createElement('img');
- img.src =url;
- img.crossOrigin = 'anonymous';
- // 必须等到图片完全加载后才能对其进行操作。浏览器通常会在页面脚本执行的同时异步加载图片。如果试图在图片未完全加载之前就将其呈现到canvas上,那么canvas将不会显示任何图片
- if(img.complete) {
- canvas.width =img.width;
- canvas.height =img.height;
- ctx.drawImage(img, 0, 0);
- } else{
- img.onload = function() {
- canvas.width =img.width;
- canvas.height =img.height;
- ctx.drawImage(img, 0, 0);
- const src = canvas.toDataURL(" image/jpeg", 0.3);
- $(drawId).attr('src', src);
- };
- }
- }
服务器针对跨域修改的图片也要做两点限制:
常见的图片跨域修改的错误有两种:
至此,关于 CORS 的一些知识点已经分享完了,总结如下:
CORS 是浏览器和服务器配合完成的跨域请求,个人认为,主要是服务端配置好后,浏览器根据服务端配置的自定义头部和提供的可以进行的 CORS 的方法来进行跨域操作。
基于以上总结,提供测试 Demo:
https://github.com/weiruifeng/fetchTest
参考资料:
HTTP 访问控制(CORS): https://developer.mozilla.org/zh-CN/docs/web/HTTP/Access_control_CORS
如有问题,欢迎大家指正。
来源: http://www.cnblogs.com/WeiRuifeng/p/6717866.html