JSAJAX跨域-被调用方与调用方解决方案

Posted H__D

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JSAJAX跨域-被调用方与调用方解决方案相关的知识,希望对你有一定的参考价值。

解决跨域问题

  跨域问题说明,参考【JS】AJAX跨域-JSONP解决方案(一)

  实例,使用上一章(【JS】AJAX跨域-JSONP解决方案(一))的实例

解决方案三(被调用方支持跨域-服务端代码解决)

  被调用方解决,基于支持跨域的解决思路,基于Http协议关于跨域的相关规定,在响应头里增加指定的字段告诉浏览器,允许调用

  跨域请求是直接从浏览器发送到被调用方,被调用方在响应头里增加相关信息,返回到页面,页面能正常获取请求内容。

  1、服务端增加一个过滤器(CrossFilter.java),过滤所有请求,在请求响应中增加内容,如下:

 1 package com.test.ajax.cross.filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.http.Cookie;
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 
15 import org.springframework.util.StringUtils;
16 
17 /**
18  * 服务端解决跨域
19  * @author h__d
20  *
21  */
22 public class CrossFilter implements Filter {
23 
24     @Override
25     public void init(FilterConfig filterConfig) throws ServletException {
26         // TODO Auto-generated method stub
27 
28     }
29 
30     @Override
31     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
32             throws IOException, ServletException {
33         HttpServletResponse res = (HttpServletResponse) response;
34 
35         HttpServletRequest req = (HttpServletRequest) request;
36         
37 
38         // 允许所有域,但不能满足带 cookie 的跨域请求
39         res.addHeader("Access-Control-Allow-Origin","*");
40         // 允许所有header
41         res.addHeader("Access-Control-Allow-Headers","*");
42         // 允许所有方法
43         res.addHeader("Access-Control-Allow-Methods", "*");
44         // 允许浏览器在一个小时内,缓存跨域访问信息(即上面三个信息)
45         res.addHeader("Access-Control-Max-Age", "3600");
46 
47         chain.doFilter(request, response);
48 
49     }
50 
51     @Override
52     public void destroy() {
53         // TODO Auto-generated method stub
54 
55     }
56 
57 }

  2、在web.xml文件中注册过滤器

<filter>
    <filter-name>CrossFilter</filter-name>
    <filter-class>com.test.ajax.cross.filter.CrossFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CrossFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

  3、编辑测试界面,test3.html

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8">
 5 <title>Insert title here</title>
 6 <script src="jquery-1.11.3.min.js" type="text/javascript"></script>
 7 </head>
 8 <body>
 9     <h2>测试服务端解决跨域问题</h2>
10     <a href="#" onclick="get()">发送get请求</a>
11 </body>
12 <script type="text/javascript">
13     function get(){
14         $.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){
15             console.log(result);
16             $("body").append("<br>" + JSON.stringify(result));
17         });
18     }
19 </script>
20 </html>

  4、在浏览器中输入地址进行访问,http://a.com:8080/test-ajax-cross/static/test3.html

    

    其他请求

      简单请求与非简单请求

      • 简单请求:浏览器先发送真正的请求后检查
      • 请求方法:GET、HEAD、POST的一种
      • 请求header:无自定义header;Content-Type为:text/plain、multipart/form-data、application/x-www-form-urlencoded的一种
      • 非简单请求:浏览器先发预检命令,检查通过后,才发送真正的请求
      • 常见的有:PUT、DELETE
      • 其它条件:发送Json格式的请求、带自定义header的请求
      • 预检命令:浏览器检测到跨域请求, 会自动发出一个OPTIONS请求, 就是所谓的预检(preflight)请求。当预检请求通过的时候,才发送真正的请求。

    A、带cookie的ajax请求,跨域问题

      1、编辑test3.html页面,增加一个带cookie的ajax请求

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8">
 5 <title>Insert title here</title>
 6 <script src="jquery-1.11.3.min.js" type="text/javascript"></script>
 7 </head>
 8 <body>
 9     <h2>测试服务端解决跨域问题</h2>
10     <a href="#" onclick="get()">发送get请求</a>
11     <a href="#" onclick="getCookie()">发送getCookie请求</a>
12 </body>
13 <script type="text/javascript">
14     function get(){
15         $.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){
16             console.log(result);
17             $("body").append("<br>" + JSON.stringify(result));
18         });
19     }
20     // 测试带上cookie的请求能否跨域
21     function getCookie(){
22         $.ajax({
23             url: "http://localhost:8080/test-ajax-cross/test/getCookie",
24             xhrFields:{
25                 // 带上证书,发送 AJAX 请求时带上 cookie
26                 withCredentials:true
27             },
28             // 允许跨域
29             crossDomain: true,
30             success:function(result){
31                 console.log(result);
32                 $("body").append("<br>" + JSON.stringify(result));
33             }
34         });
35     }
36 </script>
37 </html>

 

      2、增加接受请求的方法

1 @RequestMapping(value = "/getCookie", method = RequestMethod.GET)
2 @ResponseBody
3 public Map getCookie(@CookieValue(value = "cookie1") String cookie1) {
4     System.out.println("TestController getCookie()");
5     Map<String, Object> map = new HashMap();
6     map.put("data", "getCookie" + cookie1);
7     return map;
8 }

      3、编辑过滤器,支持带cookie的ajax请求

 1 package com.test.ajax.cross.filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13 
14 import org.springframework.http.HttpStatus;
15 import org.springframework.util.StringUtils;
16 import org.springframework.web.bind.annotation.RequestMethod;
17 
18 /**
19  * 服务端解决跨域
20  * 
21  * @author h__d
22  *
23  */
24 public class CrossFilter implements Filter {
25 
26     @Override
27     public void init(FilterConfig filterConfig) throws ServletException {
28         // TODO Auto-generated method stub
29 
30     }
31 
32     @Override
33     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
34             throws IOException, ServletException {
35         HttpServletResponse res = (HttpServletResponse) response;
36 
37         HttpServletRequest req = (HttpServletRequest) request;
38 
39 
40         // 支持所有域
41         String origin = req.getHeader("Origin");
42         if (!StringUtils.isEmpty(origin)) {
43             // 支持任何域名的跨域调用 且 支持带cookie(是被调用方域名的cookie,而不是调用方的cookie)
44             res.addHeader("Access-Control-Allow-Origin", origin);
45         }
46         // 指定允许的域,带cookie时,origin必须是全匹配,不能使用 *
47         // res.addHeader("Access-Control-Allow-Origin","http://localhost:8081");
48         // 允许所有域,但不能满足带 cookie 的跨域请求
49         // res.addHeader("Access-Control-Allow-Origin","*");
50 
51         // 支持所有header
52         res.addHeader("Access-Control-Allow-Headers","*");
53         
54         // 指定允许的方法
55 //      res.addHeader("Access-Control-Allow-Methods","GET");
56         // 允许所有方法
57         res.addHeader("Access-Control-Allow-Methods", "*");
58         // 允许浏览器在一个小时内,缓存跨域访问信息(即上面三个信息)
59         res.addHeader("Access-Control-Max-Age", "3600");
60 
61         // 允许证书,启用 cookie
62         res.addHeader("Access-Control-Allow-Credentials", "true");
63 
64                 
65         chain.doFilter(request, response);
66 
67     }
68 
69     @Override
70     public void destroy() {
71         // TODO Auto-generated method stub
72 
73     }
74 
75 }

      4、在localhost下,增加一个cookie,方法是:浏览器打开localhost:8080,按F12打开控制台,输入:document.cookie="cookie1=test"

        

      5、浏览器访问http://a.com:8080/test-ajax-cross/static/test3.html#,点击发送getCookie请求,可以看到控制台报错

        

      6、解决,响应是"Access-Control-Allow-Origin",不能用"*",通配符代替。在请求头中我们可以看到有Origin字段,后端服务可以获取这个字段的值,然后设置未允许

        

        编辑后端过滤器

      7、浏览器访问http://a.com:8080/test-ajax-cross/static/test3.html#,点击发送getCookie请求,可以看到已经能正常返回

    B、带自定义头的ajax请求,跨域问题

      1、编辑test3.html页面,增加一个带自定义头的ajax请求

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8">
 5 <title>Insert title here</title>
 6 <script src="jquery-1.11.3.min.js" type="text/javascript"></script>
 7 </head>
 8 <body>
 9     <h2>测试服务端解决跨域问题</h2>
10     <a href="#" onclick="get()">发送get请求</a>
11     <a href="#" onclick="getCookie()">发送getCookie请求</a>
12     <a href="#" onclick="getHeader()">发送getHeader请求</a>
13 </body>
14 <script type="text/javascript">
15     function get(){
16         $.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){
17             console.log(result);
18             $("body").append("<br>" + JSON.stringify(result));
19         });
20     }
21     // 测试带上cookie的请求能否跨域
22     function getCookie(){
23         $.ajax({
24             url: "http://localhost:8080/test-ajax-cross/test/getCookie",
25             xhrFields:{
26                 // 带上证书,发送 AJAX 请求时带上 cookie
27                 withCredentials:true
28             },
29             // 允许跨域
30             crossDomain: true,
31             success:function(result){
32                 console.log(result);
33                 $("body").append("<br>" + JSON.stringify(result));
34             }
35         });
36     }
37     // 测试带上不同header的请求能否跨域
38     function getHeader(){
39         $.ajax({
40             url: "http://localhost:8080/test-ajax-cross/test/getHeader",
41             headers:{
42                 "x-header1":"AAA"
43             },
44             beforeSend:function(xhr){
45                xhr.setRequestHeader("x-header2","BBB")
46             },
47             success:function(result){
48                 console.log(result);
49                 $("body").append("<br>" + JSON.stringify(result));
50             }
51         });
52     }
53 </script>
54 </html>

      2、增加自定义头的ajax请求,接受请求的方法

 1 @RequestMapping(value = "/getHeader", method = RequestMethod.GET)
 2 @ResponseBody
 3 public Map getHeader(
 4         @RequestHeader("x-header1") String header1,
 5         @RequestHeader("x-header2") String header2) {
 6     System.out.println("TestController getHeader()");
 7     Map<String, Object> map = new HashMap();
 8     map.put("data", "getHeader" + header1+header2);
 9     return map;
10 }

      3、浏览器访问http://a.com:8080/test-ajax-cross/static/test3.html#,点击发送getHeader请求,可以看到已经能正常返回

        打开F12,查看Network中,发现浏览器做了2次请求,第一是请求method是options,就是所谓的预检(preflight)请求,才发送真正的请求。且第一次OPTIONS请求,headers是不会带到后端服务器上来

        

    附:完整版过滤器

以上是关于JSAJAX跨域-被调用方与调用方解决方案的主要内容,如果未能解决你的问题,请参考以下文章

AJAX跨域问题解决方法——被调用方解决跨域

14-TypeScript简单工厂模式

ajax跨域问题解决思路

JSAJAX跨域

杂谈 —— 接口与回调的联系

RPC序列化方式优缺点