跨域问题以及Nginx配置文件

来源:互联网 发布:linux新手基础知识总结 编辑:程序博客网 时间:2024/06/17 01:26

为什么会有跨域问题

同源策略/SOP(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

SOP要求两个通讯地址的:

  • 协议 (http、https)
  • 域名 (a.com、b.com)
  • 端口号 (a.com:8080、a.com:8000)

必须相同,否则两个地址的通讯将被浏览器视为不安全的,并被block下来

前端跨域问题解决方式:

1.与服务端部署到同域上(正常部署)

正常将web项目放置到服务端项目的静态文件所在目录即可
缺陷:

  • 若服务端与web端并非相同公司或者项目组开发:需要将项目整合,然后交到服务端开发人员或者web端开发人员进行部署,部署人员可能对于另外一端的业务部署导致不必要的矛盾。
  • 若web项目的服务器分属于不同的域:可能需要将web项目拆分成不同的部分分别部署到不同的服务器上,加大了部署的困难程度。

2.CORS

同域安全策略CORS(Cross-Origin Resource Sharing)
它要求请求的服务器在响应的报头(Response Header)添加 Access-Control-Allow-Origin标签,从而允许此标签所对应域访问此服务器的资源,调用此服务器的接口。
缺陷:

  • 默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等),通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。如果服务器接收带凭据的请求,会用下面的HTTP头部来响应:
Access-Control-Allow-Credentials: true

如果发送的是带凭据的请求,但服务器的相应中没有包含这个头部,那么浏览器就不会把相应交给JavaScript,请求就无法得到结果的数据(浏览器得到了,但是我们请求的方法得不到,因为被浏览器拦截了),因此在需要传Cookie等时,服务端的Access-Control-Allow-Origin必须配置具体的具体的域名。并且还需要设置其他的请求头:

Access-Control-Allow-Origin;  //request.getHeader("Origin")||www.xxx.comAccess-Control-Allow-Methods;  //POST, GET, OPTIONS, DELETEAccess-Control-Allow-Credentials;//是否支持cookie跨域  Access-Control-Allow-Headers;//x-requested-with,content-type

Java可以参照:

public void CORSMethod(ServletRequest request, ServletResponse response) throws IOException,ServletException {    HttpServletResponse res = (HttpServletResponse) response;    res.setContentType("text/html;charset=UTF-8");       res.setHeader("Access-Control-Allow-Origin", "*");    res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");    res.setHeader("Access-Control-Max-Age", "0");       res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");       res.setHeader("Access-Control-Allow-Credentials", "true");       res.setHeader("XDomainRequestAllowed","1");}

javaScript可以参考:

$.ajax({        url: 'http://b.fdipzone.com/server.php', // 跨域        xhrFields:{withCredentials: true}, // 发送凭据        dataType: 'json',        type: 'post',        data: {'name':'fdipzone'},        success:function(ret){            if(ret['success']==true){                alert('cookie:' + ret['cookie'])              }        }    });

然后JavaScript请求的时候加上此标志:withCredentials: true。
angular可以百度:“withCredentials angularJS”

3. jsonp方式

angular可以参考:

<script>    var app = angular.module("app", []);    app.controller("ctrl", function ($scope, $http) {        $scope.jsonpAction = function () {            $http.jsonp('http://localhost/Cross_Access/JsonP.php?callback=JSON_CALLBACK')                    .success(function (data) {                        console.log(data + '---');                    });        };    });</script>

php可以参考:

<?php/** * Created by PhpStorm. * User: Shadow * Date: 2017/3/2 * Time: 09:21 *///服务端返回JSON数据$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5);$result = json_encode($arr);//动态执行回调函数$callback = $_GET['callback'];echo $callback . "($result)";

工作方式介绍:

  • 调用 $http.jsonp方法时,会根据所传参数,在当前网页创建一个”Script”标签
<script type="text/javascript" src="http://localhost/Cross_Access/JsonP.php?callback= angular.callbacks._1" async=""></script>

其中 JSON_CALLBACK 被Angular自动替换为了angular.callbacks._x _x为Angular定义的请求序列,angular会自动生成一个_x的同名方法存放到angular.callbacks 中去

  • 当标签生效http://localhost/Cross_Access/JsonP.php被下载回来时,会被当成javascript直接执行,因为我们的php端设置了返回的内容是 callback与实际数据拼接起来的一个方法,故下载的内容实际是angular.callbacks._1(xxxxxx)其会被直接执行,故若在_1(data)方法中直接将参数通过jsonp.success(function(data){})回调,那么便能直接取到相应的值

  • 移除当前网页中插入的script标签,并移除angular.callbacks中的_1(data)方法,一个完整的jsonp请求便已成功

4.代理服务器方式

前面几种方式或多或少都需要服务端人员配合,代理服务器方式可以完全不需要服务端人员配合

  • 自己写一个php,或者Java的服务端 将web项目与此项目发布到服务器,这样web项目就能直接调用此服务器的内容,然后再用此服务器调用目标服务器的内容,因为避开了浏览器的限制,因此这种方式也是能成的,但是需要注意,这种情况下因为Cookie有path和 domain的限制,因此需要截取目标服务器的Cookie,将其改为代理服务器的path和domain,否则在需要使用到Cookie的时候,由于path或domain不对,不能将Cookie转发到目标服务器。 此方式的大概流程为

这里写图片描述

  • nginx反向代理方式
    第一步: 下载安装 nginx

安装方式请百度,并确认浏览器能正常访问到 本地nginx首页:http://localhost 或 http://localhost:8080(不同的系统可能nginx的默认端口号不同)

第二步: 配置nginx环境

打开nginx对应的目录,找到nginx.conf文件,以mac为例:

cd /usr/local/etc/nginxcat nginx.conf

对于我们只需关注其中server的配置项,现将server中的注释项目全部删掉(不用担心,因为此文件还有一个nginx.conf.default的文件为默认配置的备份)

vim nginx.conf
server {        listen       8080;#监听端口号对应localhost:8080        #listen       80;#默认端口号对应localhost:80即localhost,80端口号可以省略不写        server_name  localhost;#服务名称        location / { #匹配 ‘/’时对应的配置            root   html; # 根目录对应为 相对目录 mac上为 /usr/local/opt/nginx/html             index  index.html index.htm;        }        error_page   500 502 503 504  /50x.html;        location = /50x.html {            root   html;        }}

其中 location /中配置的root为我们的web页面需要放置的目录,在mac上默认为 /usr/local/opt/nginx/html

cd /usr/local/opt/nginx/html

进入此目录可以看到里面有个index.html的文件,就是本地nginx首页:http://localhost 或 http://localhost:8080所对应的原文件,我们改了其内容,然后在浏览器刷新后能看到变化。
可以将 root 后面的 html默认的相对位置改为我们自己的目录,比如:

location / {    root /Users/Shadow/Sites;}

然后进入对应目录:

cd /Users/Shadow/Sitestouch index.htmlecho Hello nginx > index.html

保存nginx.conf并重新加载nginx配置文件:

nginx -s reload

在此之前还可以使用

nginx -t

命令检查nginx.conf文件的有效性 刷新http://localhost页面,可以看到

这里写图片描述

到此为止,nginx的环境已经配置好了

步骤三 部署项目

以testNGINX 项目为例 在/Users/Shadow/Sites/目录下新建项目,新建文件 testNginx.html文件输入以下内容:

https://m.douban.com/rexxar/api/v2/community_center?for_mobile=true

<!DOCTYPE html><html lang="en" ng-app="app"><head>    <meta charset="UTF-8">    <title>Title</title>    <script src="angular.js"></script></head><body ng-controller="myCtrl"><input type="button" value="开始请求" ng-click="action()"></body><script>    var url = "https://m.douban.com/rexxar/api/v2/community_center?for_mobile=true";    var app = angular.module("app", []).controller("myCtrl", function ($scope, $http) {        $scope.action = function () {            $http.get(url).success(function (data) {                console.log(data);            }).error(function (e) {                console.log(e);            });        }    })</script></html>

在浏览器打开
http://localhost/testNGINX/testNGINX.html
并点击开始请求按钮,然后查看日志,发生跨域问题 No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost’ is therefore not allowed access. 解决这个问题,需要修改nginx的配置文件:

cd /usr/local/etc/nginx vim nginx.conf 

在 server中添加如下内容:

location /douban/{        proxy_pass https://m.douban.com/;        proxy_cookie_path / /douban;    }

修改 testNGINX.html的请求地址为:
var url = “https://localhost/douban/rexxar/api/v2/community_center?for_mobile=true“;
重新加载nginx配置文件

 nginx -s reload

浏览器刷新
http://localhost/testNGINX/testNGINX.html
点击开始请求,查看日志可以看见请求成功了。

上面 location的配置的意思如下:
nginx截获路径为https://localhost/douban/的请求,并将请求转发到 https://m.douban.com/替换为proxy_pass配置的域名。
完整的Server配置:

server {        listen       80;        server_name  localhost;    location / {        root /Users/Shadow/Sites;    }    error_page   500 502 503 504  /50x.html;    #relative path    location = /50x.html {        root   html;    }    #absolute path    location /douban/{        proxy_pass https://m.douban.com/;        proxy_cookie_path / /douban;    }}

代理配置后完整的js请求方法

<!DOCTYPE html><html lang="en" ng-app="app"><head>    <meta charset="UTF-8">    <title>Title</title>    <script src="angular.js"></script></head><body ng-controller="myCtrl"><input type="button" value="开始请求" ng-click="action()"></body><script>    var url = "http://localhost/douban/rexxar/api/v2/community_center?for_mobile=true";    var app = angular.module("app", []).controller("myCtrl", function ($scope, $http) {        $scope.action = function () {            $http.get(url).success(function (data) {                console.log(data);            }).error(function (e) {                console.log(e);            });        }    })</script></html>

配置前后请求地址对比:

https://m.douban.com/rexxar/api/v2/community_center?for_mobile=true

http://localhost/douban/rexxar/api/v2/community_center?for_mobile=true
若配置不成功,可以优先检查如下几点:

  • nginx.conf文件是否保存并被重新加载了
  • 配置的路径是否完整,尤其需要检查后面的斜杠,斜杠有没有的差别很大,具体可以百度nginx的配置方法
  • web项目的请求地址是否改为了nginx服务器配置的地址
  • nginx首页没法打开:
    先检查nginx服务是否开启,再查看配置的根目录(root)中是否有index.html文件
  • index.html文件修改了,但是浏览器看不到变化:
    清空浏览器缓存再次查看
  • 需要发布的目录放到了根目录下,但是请求时404:
    • 查看根目录配置是否正确;
    • 请求地址是否正确;
    • 请求路径是否正确;
    • 配置文件的路径是否正确,尤其需要查看斜杠,这个特别容易忘记
  • nginx.conf文件更新了,并且重新加载了,但是请求时看不到任何变化:
    windows下,任务管理器是否打开了多个nginx的进程,可以先将nginx进程全部关闭后,重新打开一个nginx
0 0