作者:管理员  历史版本:1  更新时间:2024-05-28 11:17

一、 跨域问题解决方案

平时被问到最多的问题还是关于跨域的,其实跨域问题真的不是一个很难解决的问题。这里我来简单总结一下我推荐的几种跨域解决方案。

开发环境 生产环境
cors cors
proxy nginx

这里我只推荐这两种方式跨域,其它的跨域方式都还有很多但都不推荐,真心主流的也就这两种方式。

1.1、CORS 原理【后端解决方案】【平台采用该方法】

    我最推荐的也是IBPS平台中在使用的方式就是: cors 全称为Cross Origin Resource Sharing(跨域资源共享)。这种方案对于前端来说没有什么工作量,和正常发送请求写法上没有任何区别,工作量基本都在后端这里。每一次请求,浏览器必须先以 OPTIONS 请求方式发送一个预请求(也不是所有请求都会发送 options,展开介绍 点我),通过预检请求从而获知服务器端对跨源请求支持的 HTTP 方法。在确认服务器允许该跨源请求的情况下,再以实际的 HTTP 请求方法发送那个真正的请求。
    推荐的原因是:只要第一次配好了,之后不管有多少接口和项目复用就可以了,一劳永逸的解决了跨域问题,而且不管是开发环境还是正式环境都能方便的使用。详细 MDN 文档

1.1.1 CORS原理

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

1.1.2.CORS优缺点

CORS要求浏览器(>IE10)和服务器的同时支持,是跨域的根本解决方法,由浏览器自动完成。

优点在于功能更加强大支持各种HTTP Method,缺点是兼容性不如JSONP。
只需要在服务器端做一些小小的改造即可。

后端过滤器加类似以下代码:

//.....省略部分代码
  HttpServletResponse resp = (HttpServletResponse) response;
        //禁用缓存,确保网页信息是最新数据
        resp.setHeader("Pragma","No-cache");
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, HEAD, DELETE, PUT");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.setHeader("Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Authorization, Accept, Origin, User-Agent, Content-Range, Content-Disposition, Content-Description");
        resp.setDateHeader("Expires", -10);
//.....省略部分代码

1.1.3.CORS 例子

例如:网站http://localhost:9528/ 页面要请求http://192.168.3.230:15100/ibps/platform/v3/employee/query页面,userlist页面返回json字符串数据:{name: 'Mr.Zhuang', gender: 'male', career: 'IT Education'}

//在服务器端设置同源策略地址
request.get("/employee/query", function (req, res, next) {  
    var user = {name: 'Mr.Zhuang', gender: 'male', career: 'IT Education'}; 
    res.writeHeader(200,{"Access-Control-Allow-Origin":'http://localhost:9528'}); 
    res.write(JSON.stringify(user)); 
    res.end(); 

});

在响应头上添加Access-Control-Allow-Origin属性,指定同源策略的地址。同源策略默认地址是网页的本身。只要浏览器检测到响应头带上了CORS,并且允许的源包括了本网站,那么就不会拦截请求响应。

ps:但总有后端觉得麻烦不想这么搞,那纯前端也是有解决方案的。

1.2、proxy和nginx 【前端解决方案】

dev 开发模式下可以下使用 webpackproxy 使用也是很方便,参照 文档 就会使用了,一些个人项目使用的该方法。但这种方法在生产环境是不能使用的。在生产环境中需要使用 nginx 进行反向代理。不管是 proxynginx 的原理都是一样的,通过搭建一个中转服务器来转发请求规避跨域的问题。

1.2.1 vue-cli 的 proxy 解决方案【开发环境】

参考文档:
https://cli.vuejs.org/zh/config/#devserver-proxy

修改文件:vue.config.js

module.exports = {
  devServer: {
    ...... 省略部分代码
    proxy: {
      '^/api': {//这个是拦截第三方接口的前缀
        target: '<url>',//目标地址:如:http://10.3.2.12/
        ws: true,
        changeOrigin: true
      },
      '^/foo': {
        target: '<other_url>'//另外目标地址:如:http://10.3.2.12/
      }
    }
     ...... 省略部分代码
  }
   ...... 省略部分代码
}

<url> 配置具体的url

如:(服务器前端地址:http://10.3.2.120)
原接口地址:http://10.3.2.12/gw/resource ,这个地址存在跨域
代理后地址变成:http://10.3.2.120/proxy/gw/resource
http://10.3.2.12http://10.3.2.120/proxy 代理,访问使用代理后的地址来访问

PS:但是你访问的地址要注意了,是代理的地址,不是原服务的地址

1.2.2 Nginx 解决方案 【生产环境】

Nginx配置跨域请求 Access-Control-Allow-Origin *

当出现403跨域错误的时候No 'Access-Control-Allow-Origin' header is present on the requested resource,需要给Nginx服务器配置响应的header参数:

解决方案

通过搭建一个中转服务器来转发请求规避跨域的问题。
只需要在Nginx的配置文件 安装目录/conf/nginx.conf中配置以下参数:

location /proxy/ {
    proxy_pass http://10.3.2.12/;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }

} 

所谓中转服务器就是你需要通过nginx去代理目标接口进行转发
如:(服务器前端地址:http://10.3.2.120)
原接口地址:http://10.3.2.12/gw/resource ,这个地址存在跨域
代理后地址变成:http://10.3.2.120/proxy/gw/resource
http://10.3.2.12http://10.3.2.120/proxy 代理,访问使用代理后的地址来访问

PS:但是你访问的地址要注意了,是代理的地址,不是原服务的地址

上面配置代码即可解决问题了,不想深入研究的,看到这里就可以啦=-=

二、名词解释

1. Access-Control-Allow-Origin

服务器默认是不被允许跨域的。给Nginx服务器配置Access-Control-Allow-Origin *后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。

2. Access-Control-Allow-Headers 是为了防止出现以下错误:

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

这个错误表示当前请求Content-Type的值不被支持。其实是我们发起了”application/json”的类型请求导致的。这里涉及到一个概念:预检请求(preflight request),请看下面”预检请求”的介绍。

3. Access-Control-Allow-Methods 是为了防止出现以下错误:

Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

4.给OPTIONS 添加 204的返回,是为了处理在发送POST请求时Nginx依然拒绝访问的错误

发送”预检请求”时,需要用到方法 OPTIONS ,所以服务器需要允许该方法。

5.预检请求(preflight request)

其实上面的配置涉及到了一个W3C标准:CROS,全称是跨域资源共享 (Cross-origin resource sharing),它的提出就是为了解决跨域请求的。

跨域资源共享(CORS)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

其实Content-Type字段的类型为application/json的请求就是上面所说的搭配某些 MIME 类型的 POST 请求,CORS规定,Content-Type不属于以下MIME类型的,都属于预检请求:

application/x-www-form-urlencoded
multipart/form-data
text/plain

所以 application/json的请求 会在正式通信之前,增加一次”预检”请求,这次”预检”请求会带上头部信息 Access-Control-Request-Headers: Content-Type:

OPTIONS /api/test HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
... 省略了一些

服务器回应时,返回的头部信息如果不包含Access-Control-Allow-Headers: Content-Type则表示不接受非默认的的Content-Type。即出现以下错误:

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

三、什么是跨域


JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。那什么是跨域呢,简单地理解就是因为JavaScript同源策略的限制,a.com域名下的js无法操作b.com或是c.a.com域名下的对象。
当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。
例如:http://www.abc.com/index.html 请求 http://www.efg.com/service.php。

有一点必须要注意:跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

大家可以参照下图,有助于深入理解跨域。

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。

第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”。

四、什么是同源策略及其限制

同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。它的存在可以保护用户隐私信息,防止身份伪造等(读取Cookie)。

同源策略限制内容有:

Cookie、LocalStorage、IndexedDB 等存储性内容
DOM 节点
AJAX 请求不能发送

但是有三个标签是允许跨域加载资源:

1、<image src="xxx" >
2、<link href="xx">
3、<script src="xxx">