Nginx的反向代理跨域

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx的反向代理跨域相关的知识,希望对你有一定的参考价值。

参考技术A 什么是跨域?

跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址, b页面为域名地址,所进行的访问行动都是跨域

浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源

同ip(或domain),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,可以理解为本域脚本只能读写 本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略

现代浏览器在安全性和可用性之间选择了一个平衡点。 在遵循同源策略的基础上,选择性地为同源策略“开放了后门”。例如img script style等标签,都允许垮域引用资源,然而, 你也只能是引用这些资源而已,并不能读取这些资源的内容

同源策略限制以下几种行为:

1.Cookie、LocalStorage 和 IndexDB 无法读取

2.DOM 和 Js对象无法获得

3.AJAX 请求不能发送

http://www.domain.com/a.jshttp://www.domain.com/b.js 同一域名,不同文件或路径 允许http://www.domain.com/lab/c.jshttp://www.domain.com:8000/a.jshttp://www.domain.com/b.js 同一域名,不同端口 不允许http://www.domain.com/a.jshttps://www.domain.com/b.js 同一域名,不同协议 不允许http://www.domain.com/a.jshttp://192.168.4.12/b.js 域名和域名对应相同ip 不允许http://www.domain.com/a.jshttp://x.domain.com/b.js 主域相同,子域不同 不允许http://domain.com/c.jshttp://www.domain1.com/a.jshttp://www.domain2.com/b.js 不同域名 不允许

1、 通过jsonp跨域

2、 document.domain + iframe跨域

3、 location.hash + iframe

4、 window.name + iframe跨域

5、 postMessage跨域

6、 跨域资源共享(CORS)

7、 nginx代理跨域

8、 nodejs中间件代理跨域

9、 WebSocket协议跨域

正向代理 :代理位于网站和客户端中间, 客户端无法访问某网站,就将请求发送给代理服务器,代理从网站取回来再发送给客户端,网站并不知道为谁提供服务

反向代理 :客户端访问某网站的一个页面, 但是网站并没有,就偷偷从另外一台服务器上取回来,然后作为自己的内容吐给用户,用户不知道真正提供服务的是谁

对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过 检测url前缀,把http请求转发到后面真实的物理服务器。并通过rewrite命令把前缀再去掉。这样真实的服务器就可以正确 处理请求,并且并不知道这个请求是来自代理服务器的。

简单说,nginx服务器欺骗了浏览器,让它认为这是同源调用,从而解决了浏览器的跨域问题。又通过重写url,欺骗了真实 的服务器,让它以为这个http请求是直接来自与用户浏览器的。

Location/carrots-admin-ajax/

    proxy_passhttp://dev.admin.carrots.ptteng.com/;



proxy_pass 把请求代理到其他主机

两种写法hhttp://dev.admin.carrots.ptteng.com/    和     http://dev.admin.carrots.ptteng.com

    如果访问url = http://server/html/test.jsp ,则被nginx代理后

        情况1,将test/作为根路径,请求test/路径下的资源。

        情况2,则被nginx代理后,请求路径会变为http://proxy_pass/test.jsp,直接访问server的根资源。

是一个匹配规则,用于拦截请求,匹配任何以/proxy/html/开头的地址,匹配符合以后,停止往下搜索正则。

对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过检测url前缀,把http请求转发到后面真实的物理服务器。并通过rewrite命令把前缀再去掉。这样真实的服务器就可以正确处理请求,并且并不知道这个请求是来自代理服务器的。

简单说,nginx服务器欺骗了浏览器,让它认为这是同源调用,从而解决了浏览器的跨域问题。又通过重写url,欺骗了真实的服务器,让它以为这个http请求是直接来自与用户浏览器的。

1.执行server块的rewrite指令(这里的块指的是server关键字后包围的区域,其它xx块类似)

2.执行location匹配

3.执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件

如果循环超过10次,则返回500 Internal Server Error错误

7.参考文献

参考一: https://www.cnblogs.com/gabrielchen/p/5066120.html

参考二: http://blog.csdn.net/shendl/article/details/48443299

8.更多讨论

提问:

Q :例如img script style等标签,都允许垮域引用资源?

A :在浏览器中,并且加载的方式其实相当于一次普通的GET请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。

Q :例如img script style等标签,都允许垮域引用资源?

A :在浏览器中,并且加载的方式其实相当于一次普通的GET请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。

Q:JSONP和nginx跨域有什么不同

JSONP和nginx是完全不同的 是可以跨域的,而且在跨域脚本中可以直接回调当前脚本的函数

原理:是可以跨域的,而且在跨域脚本中可以直接回调当前脚本的函数

script标签是可以加载异域的javascript并执行的,通过预先设定好的callback函数来实现和母页面的交互。它有一个大名,叫做JSONP跨域,JSONP是JSON with Padding的略称。它是一个非官方的协议,明明是加载script,为啥和JSON扯上关系呢?原来就是这个callback函数,对它的使用有一个典型的方式,就是通过JSON来传参,即将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。JSONP只支持GET请求。

前端通过Nginx反向代理解决跨域问题

本文探讨了前端如何通过Nginx反向代理的方式解决跨域问题。

跨域

再次重申: 跨域是浏览器行为,不是服务器行为。

实际上,请求已经到达服务器了,只不过在回来的时候被浏览器限制了。就像Python他可以进行抓取数据一样,不经过浏览器而发起请求是可以得到数据,想到通过Nginx的反向代理来解决跨域问题。

代理

所谓代理就是在我们和真实的服务器之间有一台代理服务器,我们所有的请求都是通过它来进行转接的。

正向代理

正向代理就是我们访问不了Google,但是我在国外有一台vps,它可以访问Google,我访问它,叫它访问Google后,把数据传给我。

如图:

正向代理

 

反向代理

大家都有过这样的经历,拨打10086客服电话,可能一个地区的10086客服有几个或者几十个,你永远都不需要关心在电话那头的是哪一个,叫什么,男的,还是女的,漂亮的还是帅气的,你都不关心,你关心的是你的问题能不能得到专业的解答,你只需要拨通了10086的总机号码,电话那头总会有人会回答你,只是有时慢有时快而已。那么这里的10086总机号码就是我们说的反向代理。客户不知道真正提供服务人的是谁。

反向代理隐藏了真实的服务端,当我们请求 www.baidu.com 的时候,就像拨打10086一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道反向代理服务器是谁就好了,www.baidu.com 就是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。

如图:

反向代理

 

总结

  • 正向代理隐藏了真实的客户端
  • 反向代理隐藏了真实的服务器

Nginx 就是一个很好的反向代理服务器,当然apache也可以实现此功能。

windows下Apache配置参考这篇文章: Windows Apache服务器配置

Nginx

Nginx(发音同engine x)是一个 Web服务器,也可以用作反向代理,负载平衡器和 HTTP缓存。该软件由 Igor Sysoev 创建,并于2004年首次公开发布。同名公司成立于2011年,以提供支持。

我在Windows下实现Nginx负载均衡提到过Windows下Nginx命令使用。

Nginx 反向代理模块 proxy_pass

proxy_pass 后面跟着一个 URL,用来将请求反向代理到 URL 参数指定的服务器上。例如我们上面例子中的 proxy_pass https://api.shanbay.com,则将匹配的请求反向代理到 https://api.shanbay.com

通过在配置文件中增加proxy_pass 你的服务器ip,例如这里的扇贝服务器地址,就可以完成反向代理。

server {
        listen       80;
        server_name  localhost;
        ## 用户访问 localhost,则反向代理到https://api.shanbay.com
        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass https://api.shanbay.com;
        }
}

配置html以文件方式打开

一般的情况下,我们的HTML文件时放置在Nginx服务器上面的,即通过输入 http://localhost/index.html ,但是在前端进行调试的时候,我们可能是通过 使用 file:///E:/nginx/html/index.html 来打开HTML。服务器打开不是特别方便。

而我们之所以要部署在服务器上,是想要使用浏览器自带的CORS头来解决跨域问题,如果不想把HTML放置在Nginx中,而想通过本地打开的方式来调试HTML,可以通过自己添加Access-Control-Allow-Origin等http头,但是我们的AJAX请求一定要加上http://127.0.0.1/request,而不能直接是 /request,于是将nginx.conf作如下配置:

location / {
    root   html;
    index  index.html index.htm;
    # 配置html以文件方式打开
    if ($request_method = \'POST\') {
          add_header \'Access-Control-Allow-Origin\' *;
          add_header \'Access-Control-Allow-Credentials\' \'true\';
          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\';
      }
    if ($request_method = \'GET\') {
          add_header \'Access-Control-Allow-Origin\' *;
          add_header \'Access-Control-Allow-Credentials\' \'true\';
          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\';
    }
    # 代理到8080端口
    proxy_pass        http://127.0.0.1:8080;

}

处理DELETE和PUT跨域请求

而现在我的后台是restful风格的接口,采用了delete和put方法,而上面的配置就无能为力了。

可以通过增加对非简单请求的判断来解决DELETE和PUT跨域请求。

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

因此,为了使Nginx可以处理delete等非简单请求,Nginx需要作出相应的改变,更改配置如下

location / {
    # 完成浏览器的"预检"请求
        if ($request_method = \'OPTIONS\') {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Credentials true;
        add_header Access-Control-Allow-Methods \'GET, POST, PUT, DELETE, 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\';
        return 204;
        }
    # 配置html在本地打开
    if ($request_method = \'POST\') {
          add_header \'Access-Control-Allow-Origin\' *;
          add_header \'Access-Control-Allow-Credentials\' \'true\';
          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\';
      }
    if ($request_method = \'GET\') {
          add_header \'Access-Control-Allow-Origin\' *;
          add_header \'Access-Control-Allow-Credentials\' \'true\';
          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\';
    }
    root   html;
    index  index.html index.htm;
    # 配置html在Nginx中打开
    location ~* \\.(html|htm)$ {

        }
    # 代理到8080端口
    proxy_pass        http://127.0.0.1:8080;

}

我们还必须把我们的html代码放在Nginx中html文件夹内,即使用Nginx当做我们的前端服务器。

URL重写

有时候我们仅仅只想将/api下的url反向代理到后端,可以通过在nginx.conf中配置url重写规则如下:

location / {
    root   html;
    index  index.html index.htm;
         location ^~ /api {
         rewrite ^/api/(.*)$ /$1 break;
         proxy_pass https://api.shanbay.com/;
         }
    }

这样的话,我们只用处理/api下的url。

在配置文件中我们通过rewrite将URL重写为真正要请求的URL,通过proxy_pass代理到真实的服务器IP或者域名。

Cookie

如果Cookie的域名部分与当前页面的域名不匹配就无法写入。所以如果请求 www.a.com ,服务器 proxy_pass 到 www.b.com 域名,然后 www.b.com 输出 domian=b.com 的 Cookie,前端的页面依然停留在 www.a.com 上,于是浏览器就无法将 Cookie 写入。

可在nginx反向代理中设置:

location / {
    # 页面地址是a.com,但是要用b.com的cookie
    proxy_cookie_domain b.com a.com;  #注意别写错位置了 proxy_cookie_path / /;
    proxy_pass http://b.com;
}   

总结

Nginx解决跨域问题通过Nginx反向代理将对真实服务器的请求转移到本机服务器来避免浏览器的"同源策略限制"。

以上是关于Nginx的反向代理跨域的主要内容,如果未能解决你的问题,请参考以下文章

Nginx的反向代理跨域

Nginx转发+反向代理实现跨域接口转发

uniapp之h5反向代理设置踩坑,解决跨域问题

vue-cli 配置服务端口反向代理

webpack配置proxy反向代理,解决跨域问题

前端通过Nginx反向代理解决跨域问题