• nginx/ajax跨子域请求的两种现代方法以及403解决


    因为面向互联网的性质,我们公司的大部分系统都采用多子域的方式进行开发和部署,以达到松耦合和分布式的目的,因此子系统间的交互不可避免。虽然通过后台的rpc框架解决了大部分的交互问题,但有些情况下,前端直接发起的各子系统之间交互仍然不可避免。由于浏览器天然的安全性本质,早期通常是不允许直接调用不同域名下的请求。如果直接调用不同域下的请求,会报“No 'Access-Control-Allow-Origin' header is present on the requested resource”错误。

    所以在HTML 5以前,都是变相的方式绕过,主要有如下几种方式:

    JSONP

    我们发现,Web页面上调用js文件时不受是否跨域的影响,凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>。那就是说如果要跨域访问数据,就服务端只能把数据放在js格式的文件里。恰巧我们知道JSON可以简洁的描述复杂数据,而且JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据。然后客户端就可以通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件。客户端在对JSON文件调用成功之后,也就获得了自己所需的数据。这就形成了JSONP的基本概念。允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

    在angularjs中,通常是如下:

    $http.jsonp(url + "?callback=JSON_CALLBACK") .success(function(data) {
     将data赋值给$scope的某些属性
    });

    但JSONP只支持get,这样就限制了传递的数据量。所以就有了HTML 5里面的CORS。

    CORS

    Cross-Origin Resource Sharing (CORS) 是W3c工作草案,它定义了在跨域访问资源时浏览器和服务器之间如何通信。CORS背后的基本思想是使用自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否。CORS规范简单地扩展了标准的XHR对象,以允许JavaScript发送跨域的XHR请求。它会通过
    预检查(preflight)来确认是否有权限向目标服务器发送请求。

    CORS与JSONP相比,更为先进、方便和可靠。

    1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
    2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
    3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。如下:


    对一个简单的请求,没有自定义头部,要么使用GET,要么使用POST,它的主体是text/plain,请求用一个名叫Orgin的额外的头部发送。Origin头部包含请求页面的头部(协议,域名,端口),这样服务器可以很容易的决定它是否应该提供响应。
    服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。
    Header set Access-Control-Allow-Origin * 

    对于springboot(没有前置nginx时),可以如下配置:

        private CorsConfiguration buildConfig() {  
            CorsConfiguration corsConfiguration = new CorsConfiguration();  
            corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用
            corsConfiguration.addAllowedHeader("*"); // 2允许任何头
            corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等) 
            return corsConfiguration;  
        }  
      
        @Bean  
        public CorsFilter corsFilter() {  
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
            source.registerCorsConfiguration("/**", buildConfig()); // 4  
            return new CorsFilter(source);  
        }


    为了防止XSS攻击我们的服务器, 我们可以限制域,比如在nginx中可以如下设置:

    http {
    ......
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
    ......
    }

    具体参见https://michielkalkman.com/snippets/nginx-cors-open-configuration.html

    上述都配置了,nginx报“

    2019/01/24 16:14:20 [error] 18728#24984: *67 access forbidden by rule, client: 127.0.0.1, server: localhost, request: "OPTIONS /tabase/taprocess/diagram/loadAllNodeState.json?instanceId=20190121_aop_1 HTTP/1.1", host: "localhost"”,客户端报:

    Failed to load http://localhost/tabase/taprocess/diagram/loadAllNodeState.json?instanceId=20190121_aop_1: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8086' is therefore not allowed access.

    此时请求在nginx就出错了,因为nginx在windows,所以不是权限的问题。

    解决方法:

    增加如下:

             limit_except GET POST OPTIONS {
                deny  all;
             }  
    或者去掉limit_except

    Failed to load http://localhost/tabase/taprocess/diagram/loadAllNodeState.json?instanceId=20190121_aop_1: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8086', but only one is allowed. Origin 'http://localhost:8086' is therefore not allowed access. 

    nginx和后台同时包含了跨域处理,去掉一个即可。

    Failed to load http://localhost/tabase/taprocess/diagram/loadAllNodeState.json?instanceId=20190121_aop_1: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:8086' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

    因为使用了下列选项:

    xhrFields: {
    withCredentials: true
    },

    不能使用*。

    jquery.min.js:5 Failed to load http://localhost/tabase/taprocess/diagram/loadAllNodeState.json?instanceId=20190121_aop_1: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin 'http://localhost:8086' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

    增加

    add_header Access-Control-Allow-Credentials true;

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

    在Access-Control-Allow-Headers增加所有的头类型,例如:

    add_header Access-Control-Allow-Headers Origin,X-Requested-With,Content-Type,Accept;

    所以使用nginx的完整配置如下(配置nginx,就去掉tomcat的cors):

            location ^~ /tabase/ {
    			add_header Access-Control-Allow-Origin $http_origin;
    			add_header Access-Control-Allow-Headers Origin,X-Requested-With,Content-Type,Accept;
    			add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
    			add_header Access-Control-Allow-Credentials true;
             #root   html;
             #index  testssl.html index.html index.htm;
             proxy_redirect off;
             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://10.20.39.223:38080;
    		}
    # 注意,不要limit_except选项

    在angularjs中,我们可以如下调用cors请求:

    myApp.config(function($httpProvider) {
    $httpProvider.defaults.useXDomain = true;
    });
    myApp.controller("JsonController",function($scope,$http) {
    $scope.getAjax = function(url) {
    $http.get(url).success(function (response) {
    $scope.students = response;
    });
    };
    });
  • 相关阅读:
    Spring启动流程
    bash脚本
    初识RPC框架
    C++ 全局变量、局部变量、静态全局变量、静态局部变量的区别
    MacOS安装vs code并且配置C/C++环境2020
    numpy 数组操作
    numpy索引 切片和迭代
    numpy基础之数据类型
    numpy基础
    Bootstrap Navbar应用及源码解析
  • 原文地址:https://www.cnblogs.com/zhjh256/p/5843297.html
Copyright © 2020-2023  润新知