nginx转发的方式进行跨域(山的那边海的那边有一群小精灵)

582 阅读5分钟

跨域原理--同源策略

所谓"同源"指的是"三个相同",如果这三个有一个不同就会面临跨域的问题

  • 协议相同
  • 域名相同
  • 端口相同

在非同源的情况下主要有三种浏览器行为收到了限制,

  1. cookie/LocalStorage和IndexDB无法读取
    • cookie一般保存了登录信息,如果没有同源策略那么用户可以随意冒充其它用户。
    • LocalStorage里会存储一些会话信息为了安全不能被非同源读取。
    • IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。之前没有怎么用过,感觉大佬的这一篇写的很好大家可以看一看浏览器数据库 IndexedDB 入门教程
  2. DOM 无法获得
    • DOM无法获取那么也无法使用iframe获取对方的DOM和进行一些操作,也无法进行父子页面之间的通信。
  3. AJAX 请求不能发送
    • 非同源情况下不能提交ajax请求获取不同源接口的值和进行操作。

AJAX跨域解决方法

JSONP

  • 网页动态添加<script>元素,向非同源域请求JSON数据,本质上是利用拥有src属性的标签来进行跨域(script、img、iframe等标签),但是这种跨域方式是有局限的,JSONP方式只能为get请求进行跨域,除此之外均无法使用。

前端部分:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript">
      // 得到结果后的回调函数
      var Handler = function(data){
          console.log(data)
      };
      // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
      var url = "http:xx.xxx.com?id=111&callback=flightHandler";
      // 创建script标签,设置其属性
      var script = document.createElement('script');
      script.setAttribute('src', url);
      // 把script标签加入head,此时调用开始(动态添加的js不影响原本dom和js脚本的加载)
      document.getElementsByTagName('head')[0].appendChild(script); 
    </script>
  </head>
  <body></body>
</html>

服务端:

//把json传入回调函数,那么前端则可以获得当前的数据
Handler({
  "code": "CA1998",
  "price": 1780,
  "tickets": 5
});

利用ajax的话:

<script type="text/javascript">
 $(document).ready(function(){ 
     $.ajax({
     type: "get",
      url: "http:xx.xxx.com?id=111",
       dataType: "jsonp",
       //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
       jsonp: "callback",
       //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
       jsonpCallback:"Handler",
       success: function(json){
          console.log(data);
       },
       error: function(){}
    });
});
</script>

CORS 跨域资源共享

跨域资源共享 CORS 详解

nginx转发进行跨域

因为很多时候有可能会出现服务端无法设置,也无法将JSON字符串包装成JSONP可以调用的形式,例如调用国家平台的接口等操作,这时需要想办法让请求接口和开发场景同源。

下载nginx

地址:nginx下载

准备一个需要跨域的demo.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
<!--  -->
</body>
<script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<script type="text/javascript">
var settings = {
  "url": "http://check.vecc-mep.org.cn:8090/api/v10/login",//本地调用之后发现跨域了
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Content-Type": "application/json"
  },
  "data": JSON.stringify({"password":"XXXX","username":"XXXX"}),
};

$.ajax(settings).done(function (response) {
  console.log(response);
});
</script>
</html>

然后发现跨域了,山的那边海的那边不让我去..

修改nginx/conf/nginx.conf文件如下(建议不要直接复制粘贴,按照注释自己改,我这里并不是整个文件):

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    # 直接请求nginx也是会报跨域错误的这里先设置允许跨域
    # 如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了)
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        #这里是根路径,指向了index.html
        #(请将你的index.html文件放在nginx/html文件下替换原有的index.html,
        #或者你的html名称不是index的话记得来这里改一下)
        location / {
            root   html;
            index  index.html index.htm;
        }
        # 这里的api是自定义的名字,
        #代表检测到api这个路径之后会转发到我想要的地址,实现"山不过来我就过去"
        location api {
            rewrite ^/b/(.*)$ /$1 break; # 去除本地接口/api前缀, 否则会出现404
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             # 转发地址,即我想请求的对方的地址
             proxy_pass http://check.vecc-mep.org.cn:8090/api/v10;
        }
        #error_page  404              /404.html;

......

}

然后修改之后的index.html文件如下:

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
<!--  -->
</body>
<script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<script type="text/javascript">
var settings = {
  "url": "api/login",//修改为api(我自定义的辣个前缀地址)
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Content-Type": "application/json"
  },
  "data": JSON.stringify({"password":"XXXX","username":"XXXX"}),
};

$.ajax(settings).done(function (response) {
  console.log(response);
});
</script>
</html>

先双击nginx进行启动,然后在浏览器输入127.0.0.1,如果出来了你写的index.html页面,f12即可看到结果.

可能出现的错误

发现输入127.0.0.1之后显示无法访问此网站,就是没启动起来

-->先检查一下nginx的路径是否有中文,有中文可能会导致无法启动

-->检查完之后发现不是中文原因,进入nginx/logs/error.log查看错误日志,可以搜索对应的错误信息获取错误原因

-->还是找不出来,建议使用命令行打开之后输入start nginx再试试,如果logs文件夹内出现了pid.log和access.log一般就是成功了

常用nginx命令

  • start nginx 或者 nginx.exe --启动nginx

  • nginx.exe -s stop 快速停止nginx,不保存信息

  • nginx.exe -s quit 完整停止nginx,保存信息

  • nginx.exe -s reload 修改配置信息之后重新载入配置

location后面的配置问题

localtion / {
    # 以下规则匹配所有请求
    # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
    # xxx 你的配置写在这里
}

location = / {
    # 精确匹配 / ,后面带任何字符串的地址都不符合
}

localtion /api {
    # 匹配任何 /api 开头的URL,包括 /api 后面任意的, 比如 /api/getList
    # 匹配符合以后,还要继续往下搜索
    # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
}

localtion ~ /api/abc {
    # 匹配任何 /api/abc 开头的URL,包括 /api/abc 后面任意的, 比如 /api/abc/getList
    # 匹配符合以后,还要继续往下搜索
    # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
}

总结

nginx还有很多很棒的功能包括负载均衡等,本次主要是提及一下服务端无法设置的情况下利用nginx进行转发跨域的问题,错误之处希望大家积极指正,谢谢大家呀。

参考资料:浏览器同源政策及其规避方法