工具

浅谈跨域——简单了解跨域的几种方式

2018-11-12  本文已影响0人  爆发吧小宇宙

前言部分

一、跨域是什么?

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源(广义的)。

广义的跨域场景有以下几种:

我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

二、同源策略是什么?

同源策略/SOP(Same origin policy)是一种约定,由 Netscape 公司 1995 年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS(跨站脚本攻击)、CSFR(Cross-site request forgery 跨站请求伪造)等攻击。所谓同源是指 ”协议+域名+端口” 三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。

跨域的安全限制,主要是针对浏览器端来说的,服务器端是不存在跨域安全限制的。

浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。如果协议、端口和主机对于两个页面是相同的,则两个页面具有相同的源,否则就是不同源的。如果要在js里发起跨域请求,则要进行一些特殊处理了。或者,你可以把请求发到自己的服务端,再通过后台代码发起请求,再将数据返回前端。

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

三、常见的跨域场景有哪些?

URL 说明 是否允许通讯
http://www.demo.com/a.js
http://www.demo.com/b.js
http://www.demo.com/lab/c.js
同一域名,不同文件或路径 允许
http://www.demo.com:8000/a.js
http://www.demo.com/b.js
同一域名,不同端口 不允许
http://www.demo.com/a.js
https://www.demo.com/b.js
同一域名,不同协议 不允许
http://www.demo.com/a.js
http://127.0.0.1/b.js
域名和域名对应相同 IP 不允许
http://www.demo.com/a.js
http://x.demo.com/b.js
http://demo.com/c.js
主域相同,子域不同 不允许

示例说明:

四、解决方案

跨域的几种解决方案

一、jsonp 方式

jsonp (json with padding) 是 json 的一种“使用模式”,是为了解决跨域问题而产生的解决方案。

1、 jsonp 产生:

  1. AJAX 直接请求普通文件存在跨域无权限访问的问题, 尽管是静态页面;
  2. 我们在调用 js 文件的时候又不受跨域影响,比如引入 jquery 框架的时候;
  3. 凡是拥有 src 这个属性的标签都可以跨域例如 <script> <img> <iframe>;
  4. 如果想通过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里;
  5. json 是一个轻量级的数据格式,还被 js 原生支持;
  6. 为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback 参数给服务端;

2、举例:

以下是使用jQuery的jsonp发起跨域请求的例子描述,并总结。为了将过程描述清楚,内容过长,可以备一杯☕️慢慢享用~

<!--requestTest.html-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>跨域测试</title>
    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
      /* 演示端口不同引起的跨域
      *
      $(document).ready(function () {
        $("#btn").click(function () {
          $.ajax({
            // url: 'http://localhost:8001/student', // 默认的 *8001 端口站点, 数据所在位置,跨域条件达成
            // url: 'http://localhost:8080/message', // 自己配置的 users 下的 *8080 端口站点,数据所在位置,非跨域
            type: 'GET',
            success: function (data) {
              $(text).val(JSON.stringify(data))
            }
          })
        })
      })
       */
      var message = function(data) {
        console.log('执行 message 方法.返回数据为:', data)
        alert(data[1].title)
      }
    </script>
    <!--使用 jsonp 实现跨域请求,用 js 包裹数据,可以访问。(这种情况更适用于前端的处理)-->
    <!--经测试,在未拼接 callback 的情况下,浏览器执行完这俩 js 文件,自动匹配了 message 方法。效果相同-->
    <script type="text/javascript" src="http://localhost:8080/message?callback=message"></script>
  </head>
  <body>
    <input id="btn" type="button" value="跨域获取数据">
    <textarea id="text" style="width: 400px; height: 100px;"></textarea>
  </body>
</html>
/**
 * student.json
*/
[{
  "age": 22,
  "birthday": "2018-1-10 12:12",
  "id": 1,
  "major": "信息管理",
  "name": "Damon",
  "status": true
}, {
  "age": 23,
  "birthday": "2018-1-10 12:12",
  "id": 2,
  "major": "软件工程",
  "name": "John",
  "status": true
}, {
  "age": 24,
  "birthday": "2018-1-10 12:12",
  "id": 3,
  "major": "计算机科学与技术",
  "name": "Sonia",
  "status": true
}, {
  "age": 22,
  "birthday": "2018-1-10 12:12",
  "id": 4,
  "major": "计算机科学与技术",
  "name": "Mary",
  "status": true
}]

端口为8080
站点目录:


8080站点目录.png

代码如下:

/**
 * message.js
 */
console.log('服务器端执行前端传来的 message 方法。并携带参数返回。')
message([
  {"id":"1", "title":"上海新闻联播,12岁的小王竟然比年仅六岁的小李大6岁!"},
  {"id":"2", "title":"楼市告别富得流油 专家:房价下跌是大概率事件"},
  {"id":"3", "title":"股市暴跌,双十一战绩赫然,这究竟是什么鬼迷了心窍?!"},
  {"id":"4", "title":"没有运气,不要玩A股,你以为的谷底,只是下一个高地!"},
  {"id":"5", "title":"美丽新世界,啦啦啦啦啦~"},
  {"id":"6", "title":"国际要闻:听说昨天特朗普因为下个小雨没去开会!"},
  {"id":"7", "title":"易烊千玺太帅,阿姨、妈妈、女友、姐姐组成的几千万粉丝高呼请他停止散发魅力!"},
  {"id":"8", "title":"谢耳朵和艾米结婚了,好感动!"},
  {"id":"9", "title":"如果明天不下雨,竟然也不一定看到太阳!"},
  {"id":"10", "title":"没有新闻了。"}
]);

在8001端口下 requestTest.html 文件中,访问 8080端口下的文件 message.js:


8001文件请求8080中message.png

该请求报错,提示跨域不被允许:


8001访问8080message报错信息.png

在8001端口下 requestTest.html 文件中,通过 <script> 标签 包裹message.js 的请求:


8001通过script标签访问8080的message代码.png

可以看到跨域报错信息不见了,可以正常访问到数据:


8001 script方式跨域请求访问8080message运行结果.png

总结:能够正常访问数据,script 标签可以得到其他来源的数据,这也是jsonp的理论依据。缺点:只能进行get 请求,无法访问服务器的响应文本(单向请求)。

protected final static String CHARSET = ";charset=UTF-8";

    @RequestMapping(value = "/rest/public/weChat/subscription/queryAuthTaskStatus" ,method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE + CHARSET)
    @ResponseBody
    public void queryTaskStatus(HttpServletRequest request, PrintWriter out, HttpServletResponse response)throws ServletException, IOException {


        List<Student> studentList = new ArrayList();
        Student student = new Student();
        student.setName("Zhangshan");
        student.setAge("23");
        student.setMajor("前端html");
        student.setStatus(true);
        studentList.add(student);
        Student student2 = new Student();
        student2.setName("list");
        student2.setAge("20");
        student2.setMajor("java开发");
        student2.setStatus(true);
        studentList.add(student2);
        Student student3 = new Student();
        student3.setName("李明");
        student3.setAge("18");
        student3.setMajor("数据部门");
        student3.setStatus(false);
        studentList.add(student3);

        JSONArray jsonArray = JSONArray.fromObject(studentList);
        String result = jsonArray.toString();

     //前端传过来的回调函数名称
      String callback = request.getParameter("callback");


     //前端传过来的回调函数名称
        result = callback + "(" +result+")";
        response.getWriter().write(result);
        //out.write(result);

        //out.flush();
        //out.close();
    }
}

我们首先看一下采用ajax 普通方式进行请求, js 代码如下:

$ajax({
    url: "http://localhost:8082/SSM2/rest/public/weChat/subscription/query/queryAuthTaskStatus",
    type: "get",
    datatype: "json", // 指定服务器返回的数据类型
    success: function(data) {
        alert(asd);
        // var asd = JSON.stringify(data);
    }
});

普通方式请求提示跨域无法访问,结果如图:


jQuery普通方式请求服务器数据.jpg

然后我们更改代码,使用ajax jsonp方式请求,如果使用简单的方式,就只需配置 dataType: ‘jsonp’,就可以发起一个跨域请求。jsonp 指定服务器返回的数据类型为 jsonp 格式,可以看到请求的路径自动带了一个callback=xxx,xxx是 jQuery随机生成的一个回调函数名称。
服务器端代码不变,js 代码截图如下:


jQuery jsonp方式请求数据代码.png

可正常访问数据,请求结果如下(示例中json 数据解析出现乱码,暂时忽略):


jQuery jsonp 方式请求结果截图.jpg

以上的例子能简单看到 jsonp 是能够完成跨域请求的,结合前后台的配合,也更好理解怎么使用 jsonp 。

 1 <%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
 2 <html>
 3 <head>
 4     <title>跨域测试</title>
 5     <script src="js/jquery-1.7.2.js"></script>
 6     <script>
 7 
 8         function showData (data) {
 9             console.info("调用showData");
10 
11             var result = JSON.stringify(data);
12             $("#text").val(result);
13         }
14 
15         $(document).ready(function () {
16 
17 //            window.showData = function  (data) {
18 //                console.info("调用showData");
19 //
20 //                var result = JSON.stringify(data);
21 //                $("#text").val(result);
22 //            }
23 
24             $("#btn").click(function () {
25 
26                 $.ajax({
27                     url: "http://localhost:9090/student",
28                     type: "GET",
29                     dataType: "jsonp",  //指定服务器返回的数据类型
30                     jsonpCallback: "showData",  //指定回调函数名称
31                     success: function (data) {
32                         console.info("调用success");
33                     }
34                 });
35             });
36 
37         });
38     </script>
39 </head>
40 <body>
41     <input id="btn" type="button" value="跨域获取数据" />
42     <textarea id="text" style="width: 400px; height: 100px;"></textarea>
43 
44 </body>
45 </html>

回调函数可以写到<script>里(默认属于window对象),或者指明写到window对象里,看jQuery源码,可以看到jQuery调用回调函数时,是调用的window.callback。代码如上,看调用结果发现,请求时带的参数是callback=showData,然后再调用了success【2,文末注解】。所以success是返回成功以后必定会调的函数。

如果想要更改 callback 这个参数的名称,参考以下代码第23行。

 1 <%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
 2 <html>
 3 <head>
 4     <title>跨域测试</title>
 5     <script src="js/jquery-1.7.2.js"></script>
 6     <script>
 7 
 8         function showData (data) {
 9             console.info("调用showData");
10 
11             var result = JSON.stringify(data);
12             $("#text").val(result);
13         }
14 
15         $(document).ready(function () {
16 
17             $("#btn").click(function () {
18 
19                 $.ajax({
20                     url: "http://localhost:9090/student",
21                     type: "GET",
22                     dataType: "jsonp",  //指定服务器返回的数据类型
23                     jsonp: "theFunction",   //指定参数名称
24                     jsonpCallback: "showData",  //指定回调函数名称
25                     success: function (data) {
26                         console.info("调用success");
27                     }
28                 });
29             });
30 
31         });
32     </script>
33 </head>
34 <body>
35     <input id="btn" type="button" value="跨域获取数据" />
36     <textarea id="text" style="width: 400px; height: 100px;"></textarea>
37 
38 </body>
39 </html>

如此以来后台也要跟着改变,找到我前面的例子jQuery 的 jsonp 方式跨域请求中后台代码,找到下图对应位置并作出修改:

后台修改代码提示
把getParameter(“callback”)里的callback改成前面23行代码配置的函数名“theFunction”即可。

经测试,无法进行POST请求,要测试就将前面的请求方式更改成POST即可,结果如下:


jsonp进行POST请求报错信息

jsonp 本质就是执行了JavaScript。是通过 script 标签的开放策略,使网页可以获取其他来源的数据,用 jsonp 获取的数据也不是真正的 json,而是任意的JavaScript, 用JavaScript解释器,而不是用json解析器解析,ajax 只是对脚本请求做了封装。所以,ajax 的 jsonp 请求也是不支持 POST 的。在谷歌浏览器Chrome中查看 jsonp 发送的请求都是js类型,而不是 xhr【1,文末注解】 :【图片来源自水印地址】


请求类型示意图

3、总结

综上一大堆解释和示例,我们可以对 jsonp 原理作如下简单描述:
以下例子是较早学习跨域看到的描述,觉得写得很好,当时只记录了这个片段,未保存出处,若有人看到过望在评论指出,我会补上。也在此对作者表达歉意,并检讨以后摘录要记下出处

首先我们假设a网页调用b网站的服务

  1. a 网站需要准备一个方法,例如 callback(args);
  2. a 网站在页面中插入一个 <script> 标签,src 指向 b 网站的地址,并带上callback 作为参数;
  3. b网站接受请求处理后,把结果和回调方法的名字组成一个字符串返回,例如callback(‘data’);
  4. 由于是 script 标签,b 网站返回的字符串会被当成js解析执行,相当于调用到了 callback 方法;
  5. 主要利用了 script(img, iframe等有src属性的标签)可以跨站点访问的特性,且只能用 GET 请求,需要服务端做点配合,并且需要信任服务器(安全考虑)。jquery 的 jsonp ajax 只是封装了这个过程,让你看上去和普通 ajax 没什么区别,其实却一点关系都没有。

二、跨域资源共享

跨域资源共享(CORS)是一种网络浏览器的技术规范,它为web服务器定义了一种方式,允许网页从不同的域访问资源。CORS就是为了让AJAX可以实现可控的跨域访问而生的。

1、内部机制讲解

1) 简介

通过在HTTP Header中加入扩展字段,服务器在相应网页头部加入字段表示允许访问的domain和HTTP method,客户端检查自己的域是否在允许列表中,决定是否处理响应。

CORS 需要浏览器和服务器同时支持。目前所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

2) 两种请求

浏览器将 cors 请求分为两种请求:简单请求 和 非简单请求。简单请求就是使用设定的请求方式请求数据
而非简单请求则是在使用设定的请求方式请求数据之前,先发送一个OPTIONS请求,看服务端是否允许客户端发送非简单请求.只有"预检"通过后才会再发送一次请求用于数据传输。(例子,get、put 请求方式的区别)。

只要同时满足以下两大条件,就属于简单请求。

(1) 请求方法是以下三种方法之一:
    HEAD
    GET
    POST
(2)HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

3) 简单请求:

浏览器对于简单的请求,直接发起 cors 请求。具体的说,就是在请求头加上 Origin 字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。(ps: 我理解具体就是请求的回应状态为200,但是会在控制台提示错误信息,access-origin-check)

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。注意这几个是响应头里的,由服务器端设置。

上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。另一方面,需要前端,在请求的时候设置withCredentials属性,才能让浏览器处理携带 cookie 发起请求。

注意:

如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

4)非简单请求

2、如何实现 CORS

只需要在后台中加上配置来允许跨域请求。如果还需要携带cookie在前端被请求的Response header中加入允许携带配置,就可以实现跨域访问了!

以下也是后台小伙伴友情提供,这里再次感谢~(__) 嘻嘻……
下面我们来看一下 Java 的Tomcat 配置 cors:
首先需要下载 jar 包cors-filter与java-property-utils:

<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/cors-filter -->
<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/java-property-utils -->
<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>java-property-utils</artifactId>
    <version>1.10</version>
</dependency>

修改web.xml, 增加以下代码(最好放在其他filter前边)

<filter>         
    <filter-name>CORS</filter-name>  
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>  
    <init-param>  
     <param-name>cors.allowOrigin</param-name>  
        <param-value>*</param-value>  
    </init-param>  
    <init-param>  
     <param-name>cors.supportedMethods</param-name>  
        <param-value>GET, POST, HEAD, PUT, DELETE</param-value>  
    </init-param>  
    <init-param>  
     <param-name>cors.supportedHeaders</param-name>  
        <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>  
    </init-param>  
    <init-param>  
        <param-name>cors.exposedHeaders</param-name>  
        <param-value>Set-Cookie</param-value>  
    </init-param>  
    <init-param>  
        <param-name>cors.supportsCredentials</param-name>  
        <param-value>true</param-value>  
    </init-param>
</filter>  
  
<filter-mapping>  
    <filter-name>CORS</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping>

jQuery请求示例:

$.ajax("url", {
    type: "POST",
    xhrFields: {
        withCredentials: true,
        useDefaultXhrHeader: false
    },
    data: {
        type: "test"
    },
    dataType: 'json',
    crossDomain: true,
    success: function(data, status, xhr) {
        console.log(data);
    }
});

跨域请求默认不会携带 cookie 信息,如果要携带请配置以下信息:

// 前端设置
“withCredentials”: true 

Vue 框架 axios 配置:

axios.defaults.withCredentials = true

如果前端访问成功,则说明后台配置正确,反之,说明后台配置失败(前端可以无顾忌的甩锅给后台小伙伴,hiahia~)。

3、 CORS 与 JSONP 的比较:

4、CORS主要应用场景:

三、nginx 反向代理接口跨域

有反向代理,那就肯定有正向代理。我们先来简单说说这个正、反向代理是个啥。

1、正向代理原理

正向代理类似一个跳板机,把浏览器访问过程委托给代理去做,代理访问外部资源。


正向代理原理示意图

举个例子:
我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从目标网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
类似场景比如我们在外网去访问公司内网服务器B,我们先设置VPN,通过VPN将我们的请求转发到内网的A服务器,然后A把请求发到B上,响应内容返回到A,再由A通过VPN返回到我们。
工作流程可以描述为:
用户设置代理服务器,用户访问url,代理服务器代替用户访问并将网页内容返回。

2、反向代理服务器工作原理

反向代理(Reverse Proxy)方式是指后台内部网络服务器委托代理服务器,以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
用户访问的是代理服务器,前端是不知道后台真实地址,只知道代理地址。

反向代理原理示意图

再举个栗子:
我是一个用户,我可以访问某一个网站,网站的数据是来源于我访问不到的内部网络上的内容服务器,内容服务器设置了可以访问自己的代理服务器。于是我向目标内容服务器发起请求,其实我访问的是内容服务器设置的代理服务器,这个代理服务器将我的请求转发到目标内容服务器上,获取到数据后再返回给网站上,我就可以看见了。


反向代理工作流程示意图

工作流程可以描述为:
和正向代理相反,由目标内容服务器设置代理服务器,代理转发用户发起的请求,获取数据再返回给用户。

3、使用 nginx 反向代理解决跨域

Nginx (engine x) 是一个高性能的HTTP反向代理服务,也是一个IMAP/POP3/SMTP服务。

我们前面提到跨域是浏览器的同源策略导致的,同源策略它是浏览器针对脚本攻击采取的一种安全策略,并不是 HTTP 协议的一部分。所以服务器端调用 HTTP 接口只是使用了 HTTP 协议,是不会执行 js 脚本的,不需要同源策略,也就不会形成跨域问题。
我们使用代理(同源)服务器发起请求,再由代理(同源)服务器请求内部服务器。

我们先来看看怎么来设置反向代理实现跨域请求。

<h2>Index</h2>
<div id="show"></div>
<script type="text/javascript">
        $(function () {
            $.get("http://localhost:82/api/values", {}, function (result) {
                $("#show").html(result);
            })
        })
# 服务器的集群
    upstream  rj.nginx.com {  # 服务器集群名字 
        server    127.0.0.1:8001  weight=1;#服务器配置   weight是权重的意思,权重越大,分配的概率越大。
        server    127.0.0.1:8002  weight=2;
    }
    
    # 当前的Nginx的配置
    server {
        listen       80; #监听80端口,可以改成其他端口
        server_name  localhost; # 当前服务的域名

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            proxy_pass http://rj.nginx.com;
            proxy_redirect default;
        }

修改后代码:

server {
       listen       80; #监听80端口,可以改成其他端口
       server_name  localhost; # 当前服务的域名

       #charset koi8-r;

       #access_log  logs/host.access.log  main;
       # 单一代理
   location /apis { #添加访问目录为/apis的代理配置
       rewrite  ^/apis/(.*)$ /$1 break;
       proxy_pass   http://localhost:82;
      }
# 以下配置省略

如果要实现前端跨域携带cookie的则在 location中另外配置:

# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
 add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
 add_header Access-Control-Allow-Credentials true;

如果是在浏览器中访问,项目访问地址需要和nginx代理同源,可以在同一个站点或者对nginx实现cors相关配置。
修改代码片段解析:

  1. 由配置信息可知,我们让nginx监听localhost的80端口,网站A与网站B的访问都是经过localhost的80端口进行访问;
  2. 我们特殊配置了一个“/apis”目录的访问,并且对url执行了重写,最后使以“/apis”开头的地址(访问时的地址)都转到“http://localhost:82”(目标服务器地址)进行处理;
  3. rewrite ^/apis/(.)/1 break;
    rewrite代表重写拦截进来的请求,并且只能对域名后边以“/apis”开头的起作用,例如www.a.com/apis/msg?x=1重写。只对/apis重写。
    rewrite后面的参数是一个简单的正则 ^/apis/(.
    ),1代表正则中的第一个(),$2代表第二个()的值,以此类推。
    break代表匹配一个之后停止匹配。
<h2>Index</h2>
<div id="show"></div>

<script type="text/javascript">
        $(function () {
            $.get("/apis/api/values", {}, function (result) {
                $("#show").html(result);
            })
        })
</script>

然后在浏览器中访问B的数据就可以成功获取了。

四、nodejs 中间件代理跨域

node中间件实现跨域代理,原理大致与nginx相同,都是通过开启一个代理服务器(同源),实现数据的转发。
由于我所试验 nodejs 跨域的例子是已有的Vue项目,配置代理后实现了跨域获取数据,下面的例子分“非 Vue”和“Vue”两种进行说明。

  1. 非 Vue 框架的跨域
    利用node + express + http-proxy-middleware搭建一个proxy服务器。

express是基于 Node.js 平台,快速、开放、极简的 web 开发框架。
http-proxy-middleware 是专门用于 http 代理的一个 node 中间件,适用于connect, express, browser-sync 等等,由热门的http-proxy 驱动。

"use strict";
const express = require('express');
const path = require('path');
const app = express();
const request = require('request');
 
// 配置静态文件服务中间件
let serverUrl='http://192.168.1.220:8080'; // 目标后端服务地址
app.use(express.static(path.join(__dirname, './'))); //静态资源 index.html 和node代码在一个目录下
app.use('/', function(req, res) {
  let url = serverUrl + req.url; // req.url 传入的接口路径
  req.pipe(request(url)).pipe(res);
});

app.listen(3000,'127.0.0.1', function () {//前端 ajax 地址写 http://127.0.0.1:3000/
  console.log('server is running at port 3000'); // 3000为将要启动的端口
});
  1. Vue 框架的跨域
    利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。

npm install --save-dev express http-proxy-middleware

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/gxtz-server-web/': {
        tartget: 'http://192.168.1.220',
        changeOrigin: true,
        pathRewrite: {'^/api/': '/'}
      }
    },

    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: '#source-map',

    #其他省略
  },
}

配置相关属性含义和前面 nginx 中相同。这样就可以进行代理了。

五、WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
关于WebSocket我没还没有自己做实例去验证跨域,但是WebSocket本身支持跨域,只要会使用就行,之前写小程序使用过,但是代码封装程度高不适合作为例子看。以下例子是从前端常见跨域解决方案(全) 摘抄来的,可以参考一下。

<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');

// 连接成功处理
socket.on('connect', function() {
    // 监听服务端消息
    socket.on('message', function(msg) {
        console.log('data from server: ---> ' + msg); 
    });

    // 监听服务端关闭
    socket.on('disconnect', function() { 
        console.log('Server socket has closed.'); 
    });
});

document.getElementsByTagName('input')[0].onblur = function() {
    socket.send(this.value);
};
</script>
var http = require('http');
var socket = require('socket.io');

// 启http服务
var server = http.createServer(function(req, res) {
    res.writeHead(200, {
        'Content-type': 'text/html'
    });
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

// 监听socket连接
socket.listen(server).on('connection', function(client) {
    // 接收信息
    client.on('message', function(msg) {
        client.send('hello:' + msg);
        console.log('data from client: ---> ' + msg);
    });

    // 断开处理
    client.on('disconnect', function() {
        console.log('Client socket has closed.'); 
    });
});

结尾

简单的总结概括:

其他的没有详细描述。在使用过程中就能够知晓很明确的使用场景,后续我再陆续补充。

参考链接

如果有没理解的也可以去这里找找灵感~~
前端常见跨域解决方案(全)
jQuery jsonp跨域请求
cors跨域解析-阮一峰

关于 iframe 和 postMessage 跨域我没有自己实践,详情请查阅:
浏览器同源政策及其规避方法-阮一峰

说好的多端口配置:
Mac 自带 Apache 多端口配置


【注解1】使用XMLHttpRequest (XHR)对象可以与服务器交互。您可以从URL获取数据,而无需让整个的页面刷新。这使得Web页面可以只更新页面的局部,而不影响用户的操作。

尽管名称如此,XMLHttpRequest可以用于获取任何类型的数据,而不仅仅是XML,它还支持 HTTP以外的协议(包括文件和ftp)。

【注解2】

result = callback + "(" +result+")"; // 绑定前端传入回调函数
response.getWriter().write(result); // 后台将数据作为响应带回。不加这一句前端指定的callback对应函数能拿到数据,但是ajax请求success中是获取不到数据的。

对于更详细的XMLHttpRequest请求内容可以参考:使用XMLHttpRequest


上一篇下一篇

猜你喜欢

热点阅读