前端跨域问题总结

1,740 阅读7分钟

首先,浏览器为何要采用同源策略对跨域请求进行限制?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造。限制跨域是指限制两个网站相互读写对方的数据,合理性显而易见。可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。


更多历史背景和实际案例可参照知乎链接:

https://www.zhihu.com/question/26379635


其次,常用解决方案:

前端项目集成nodejs - request模块

本地localhost启动一nodejs服务,添加一路由,代码举例如下:

router.get('/sina', function(req, res, next) { 
    console.log('req.headers: ', req.headers);  
    request('http://sina.com', function(error, response, data) {    
        console.log('error: ', error);    
        console.log('response: ', response.statusCode);    
        console.log('typeof(data): ', typeof(data));    
        console.log('data: ', data.length);    
        res.send({body: 'body'})
    })
})

此场景下,http://sina.com是可以被成功访问的。个人理解就是此处是从一本地服务器访问sina.com,相当于规避掉了浏览器的跨域访问限制。且前端项目集成了nodejs后,可在nodejs中去管理维护后端接口的调用,同时可在Linux服务器上采用PM2工具,单独部署项目。


被调用方 - NGINX解决方案

虚拟主机:多个域名指向同一个服务器,服务器根据不同域名,将请求转向不同应用服务器,看上去好像有多个主机,实际上只有一个主机。


先进行被调用方虚拟主机的配置:

先进行host的配置:

打开windows下的hosts文件,编辑,先映射一个本地域名:

127.0.0.1 b.com

用b.com表示被调用方的域名。

然后打开nginx下的conf目录,新建vhost文件夹,用于存放虚拟主机的配置文件,实际工作中一般就采用这种习惯;打开nginx.conf文件,在最后一行添加代码

include vhost/*.conf;

要nginx载入这个目录下面所有.conf结尾的文件。

在vhost中新建b.com.conf文件,用nginx的语法在其中增加一节点:


作用是,将所有请求转到localhost:8080,监听80端口,命名为b.com。

在nginx-1.11.5目录下打开命令行窗口:

先运行nginx.exe -t,测试一下配置文件,测试成功后运行start nginx.exe启动nginx,启动成功后进行测试:

将原来的访问url开头的localhost:8080改为b.com,运行后若依然能正常访问,代表测试成功,说明虚拟主机配置完成了。

因为这次是要在nginx上实现filter的功能,则先将原来的后台服务端的filter功能删除掉,

将注册filter的配置注释掉:


将下面的逻辑移到nginx中:


其中3个为固定值,另外两个由请求的头来决定,

改为:


将预检命令的逻辑处理也添加其中,在nginx中直接返回,就不需要转到应用服务器了。

运行nginx.exe -t,测试一下配置文件。

运行nginx.exe -s reload对nginx进行重新载入。

原来html中base url是通过localhost:8080直接访问的被调用方的应用服务器,现在将localhost:8080改为b.com,即变成了html中去访问被调用方的nginx http服务器了。

发送的cookie是被调用方的域名的cookie。在b.com下添加document.cookie='cookie1=xxx'


被调用方 - apache解决方案

先运行nginx.exe -s stop将nginx停止掉,然后刷新b.com的请求,请求失败,说明nginx被停掉了。接下来进行apache的配置:

与nginx一样,我们先进行虚拟主机的配置,进入Apache24/conf目录下,打开httpd.conf文件,

先打开虚拟主机的相关配置,搜索vhost,将LoadModule...mod_vhost_alias.so注释打开,

将配置文件Include conf/extra/httpd-vhosts.conf注释也打开,保存httpd.conf。

找到对应的虚拟主机配置文件,即conf/extra/httpd-vhosts.conf,打开,文件中的每一个节点就是一个虚拟主机,将最后一个节点的代码复制一份并添加到最后,删掉无用的前两行,ServerName改为b.com,日志改为b.com-error.log...。在最后一行增加一个代理,让它把我们的请求转发过去,

ProxyPass / http://localhost:8080/

然后将此配置文件保存。因为此处使用了Proxy模块,所以需将文件中Proxy模块也打开,搜一下,将140行

LoadModule proxy_module modules/mod_proxy.so

打开,还需要把149行的

LoadModule proxy_http_module modules/mod_proxy_http.so

打开,保存文件。

键入Apache/bin目录,双击httpd.exe,弹出窗口,Apache启动成功,刷新下b.com的请求,此时b.com可正常访问了,表示虚拟主机配置成功了。

来测试一下,刷新下测试用例,看到为全部失败,接下来需要在Apache上配置下响应头,

增加支持跨域的响应头:

继续编辑httpd-vhosts.conf文件,在ProxyPass下增加代码:

Apache的配置比较复杂:


需要将httpd.conf中Headers和Rewrite模块打开:

搜索headers,将118行打开

LoadModule headers_module modules/mod_headers.so

搜索rewrite,将158行打开

LoadModule rewrite_module modules/mod_rewrite.so

保存修改后的两个文件。

将Apache关掉,双击重启,刷新测试用例,此时测试用例全部成功,表示此时的Apache支持跨域了,Apache的配置到此结束。


被调用方 - Spring框架解决方案

之前的方案都和框架无关,比如filter,是每个web应用都有的。如果采用了Spring框架,那么解决方案就变得简单起来,增加相应注解即可,请求也不需要再经过中间的http服务器了。

所以,首先将html中的base url b.com改回为localhost:8080,修改服务器后台代码,在TestController中增加注解,

@CrossOrogin
public class TestController {

保存,

再次刷新测试用例,可以看到,测试用例全部通过。

@CrossOrogin可以加在类上面,也可以加在具体的方法上面。加在类上即代表此类的所有方法都支持跨域;它还可以进行一些额外的配置,但一般场景是不需要额外配置的。


调用方解决跨域 - 隐藏跨域 - nginx配置

当你无法修改被调用方的时候,就要在调用方做文章。请求是经由调用方的http服务器的反向代理,转发到被调用方的服务器的,在浏览器上面,看不到任何的跨域请求。

那么,什么是反向代理呢?

简单来说,就是你访问同一个域名的两个不同url,它们最后会去到两个不同的服务器,我们通过接下来的测试来理解这个概念:

先看一下反向代理在nginx上是怎样配置的:

配置之前,先把测试环境还原成不支持跨域调用,

把注解@CrossOrigin删除掉:

接下来,在

C:\Windows\System32\drivers\etc\hosts文件中增加一个host,

将原127.0.0.1 b.com改为

127.0.0.1 b.com a.com

用a.com表示调用方的虚拟主机。

然后,在nginx-1.11.5\conf\vhost中新建配置文件a.com.conf

域名为a.com,将所有请求转发到81端口,

加一个代理,把我们要调用的服务器的地址代理成ajaxserver


将html中的base url改为/ajaxserver

运行start nginx.exe启动nginx

运行nginx -s reload重新加载配置,访问a.com,可以看到3个成功,但getCookie失败了,

是因为新的域名里没有cookie的信息,在a.com下添加document.cookie="cookie1=xxx"

再次刷新,测试用例全部成功,跨域问题得到了解决。

看一下这种隐藏跨域与之前的支持跨域最大不同是什么?

就是我们调用的base url。

隐藏跨域下面调用的url都是本域的,所以base url处为相对地址,

而之前支持跨域里面,base url处写的必须是绝对地址,这就是最大的不同。

浏览器看到都是相对地址,都是同一个域的地址,所以不会有任何跨域问题。


调用方解决跨域 - 隐藏跨域 - Apache配置

Apache反向代理配置:

配置的目的就是增加一个虚拟主机,在虚拟主机中把跨域请求做一个代理,

找到Apache的虚拟主机配置文件conf/extra/httpd-vhosts.conf

增加一个节点:


将base url改为ajaxserverapache

保存。将nginx.exe -s stop停掉,启动Apache服务,

运行a.com,测试成功,

且发现Request URL为http://a.com/ajaxserverapache/getCookie

Apache反向代理配置完成。


注:以上文章部分整理自慕课网教学视频:

https://www.imooc.com/video/16591

https://www.imooc.com/video/16592