【JS】AJAX跨域-被調用方與調用方解決方案(二)

解決跨域問題

  跨域問題說明,參考【JS】AJAX跨域-JSONP解決方案(一)javascript

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

解決方案三(被調用方支持跨域-服務端代碼解決)

  被調用方解決,基於支持跨域的解決思路,基於Http協議關於跨域的相關規定,在響應頭裏增長指定的字段告訴瀏覽器,容許調用前端

  跨域請求是直接從瀏覽器發送到被調用方,被調用方在響應頭裏增長相關信息,返回到頁面,頁面能正常獲取請求內容。java

  一、服務端增長一個過濾器(CrossFilter.java),過濾全部請求,在請求響應中增長內容,以下:jquery

 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 }

  二、在web.xml文件中註冊過濾器nginx

<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>

  三、編輯測試界面,test3.htmlweb

 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>

  四、在瀏覽器中輸入地址進行訪問,http://a.com:8080/test-ajax-cross/static/test3.htmlajax

    

    其餘請求

      簡單請求與非簡單請求spring

      • 簡單請求:瀏覽器先發送真正的請求後檢查
      • 請求方法: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請求,跨域問題

      一、編輯test3.html頁面,增長一個帶cookie的ajax請求apache

 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>

 

      二、增長接受請求的方法

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 }

      三、編輯過濾器,支持帶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 }

      四、在localhost下,增長一個cookie,方法是:瀏覽器打開localhost:8080,按F12打開控制檯,輸入:document.cookie="cookie1=test"

        

      五、瀏覽器訪問http://a.com:8080/test-ajax-cross/static/test3.html#,點擊發送getCookie請求,能夠看到控制檯報錯

        

      六、解決,響應是"Access-Control-Allow-Origin",不能用"*",通配符代替。在請求頭中咱們能夠看到有Origin字段,後端服務能夠獲取這個字段的值,而後設置未容許

        

        編輯後端過濾器

      七、瀏覽器訪問http://a.com:8080/test-ajax-cross/static/test3.html#,點擊發送getCookie請求,能夠看到已經能正常返回

    B、帶自定義頭的ajax請求,跨域問題

      一、編輯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>

      二、增長自定義頭的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 }

      三、瀏覽器訪問http://a.com:8080/test-ajax-cross/static/test3.html#,點擊發送getHeader請求,能夠看到已經能正常返回

        打開F12,查看Network中,發現瀏覽器作了2次請求,第一是請求method是options,就是所謂的預檢(preflight)請求,才發送真正的請求。且第一次OPTIONS請求,headers是不會帶到後端服務器上來

        

    附:完整版過濾器

 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,可是第一次OPTIONS請求,headers是不會帶過來的
52 //        String headers = req.getHeader("Access-Control-Allow-Headers");
53 //        if (!StringUtils.isEmpty(headers)) {
54 //            // 容許全部header,自定義頭訪問出現問題
55 //            res.addHeader("Access-Control-Allow-Headers", headers);
56 //        }
57         // 支持全部header
58         res.addHeader("Access-Control-Allow-Headers","*");
59         
60         // 指定容許的方法
61 //      res.addHeader("Access-Control-Allow-Methods","GET");
62         // 容許全部方法
63         res.addHeader("Access-Control-Allow-Methods", "*");
64         // 容許瀏覽器在一個小時內,緩存跨域訪問信息(即上面三個信息)
65         res.addHeader("Access-Control-Max-Age", "3600");
66         // 容許證書,啓用 cookie
67         res.addHeader("Access-Control-Allow-Credentials", "true");
68 
69         // 第一次OPTIONS請求,headers是不會帶過來的
70         if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
71             res.setStatus(HttpStatus.OK.value());
72         }
73                 
74         chain.doFilter(request, response);
75 
76     }
77 
78     @Override
79     public void destroy() {
80         // TODO Auto-generated method stub
81 
82     }
83 
84 }
View Code

 

    

解決方案三(被調用方支持跨域-Spring框架解決)

  Spring Framework 4.2 GA爲CORS提供了第一類支持。因此springMVC的版本要在4.2或以上版本才支持@CrossOrigin,@CrossOrigin能夠用在類上和方法上

  @CrossOrigin的做用就至關於上例中的CrossFilter.class

1 @Controller
2 @RequestMapping("/test")
3 @CrossOrigin
4 public class TestController {
5     ...
6 }

 

 

解決方案三(被調用方支持跨域-代理方案解決)

  在不少項目的架構中,都是使用了代理的機制,瀏覽器訪問nginx,nginx訪問tomcat服務,以下圖:

    

  那麼在上例中能夠想到,跨域請求是直接從瀏覽器發送到被調用方,被調用方在響應頭裏增長相關信息,能夠在tomcat應用服務程序中加,也能夠在nginx/apache等代理中加

  至關於nginx/apache作了過濾器的功能

  流程:

  一、修改頁面訪問地址,統一請求到http://b.com/test-ajax-cross,讓請求通過代理服務器nginx/apache

  二、修改代理服務器nginx/apache配置。

  Nginx配置以下:

 1 server{                                                                                                              
 2     listen 80;                                                                                                       
 3     server_name b.com;                                                                                               
 4                                                                                                                      
 5     location /{                                                                                                      
 6         proxy_pass http://localhost:8080/;                                                                           
 7                                                                                                                      
 8         add_header Access-Control-Allow-Methods *;                                                                   
 9         add_header Access-Control-Max-Age 3600;                                                                      
10         add_header Access-Control-Allow-Credentials true;                                                            
11                                                                                                                      
12         add_header Access-Control-Allow-Origin $http_origin;                                                         
13         add_header Access-Control-Allow-Headers *;                                                                   
14                                                                                                                      
15         # 預檢請求,直接返回正確                                                                                     
16         if ($request_method = OPTIONS){                                                                              
17             return 200;                                                                                              
18         }                                                                                                            
19     }                                                                                                                
20 }  

  Apache配置

  • 修改conf/httpd.conf找到LoadModule vhost_alias_module module/mod_vhost_alias.so取消註釋
  • 修改conf/httpd.conf找到LoadModule proxy_module module/mod_ proxy.so取消註釋
  • 修改conf/httpd.conf找到LoadModule proxy_http_module module/mod_ proxy_http.so取消註釋
  • 修改conf/httpd.conf找到LoadModule headers_module module/mod_ headers.so取消註釋
  • 修改conf/httpd.conf找到LoadModule rewrite_module module/mod_ rewrite.so取消註釋
  • 修改conf/httpd.conf找到Include conf/extra/httpd-vhosts.conf取消註釋
  • 修改conf/extra/httpd-vhosts.conf在最後面增長下面的內容便可
<VirtualHost *:80>
    ServerName b.com
    ErrorLog "logs/b.com-error.log"
    CustomLog "logs/b.com-access.log" common
    ProxyPass / http://localhost:8080/

    # 把請求頭的origin值返回到Access-Control-Allow-Origin字段
    Header always set Access-Control-Allow-Origin "expr=%{req:origin}"

    Header always Access-Control-Allow-Headers "*"

    Header always set Access-Control-Allow-Methods "*";
    Header always set Access-Control-Max-Age "3600";
    Header always set Access-Control-Allow-Credentials ""true";

    # 處理預檢命令OPTIONS,直接返回204
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD}OPTIONS
    RewriteRule ^(.*)$"/" [R=204,L]
</VirtualHost>

 

解決方案四(被調用方支持跨域)

  使用nginx進行反向代理,結構以下

  

  當頁面要進行跨域請求是,能夠由nginx對url進行過濾,跨域的url請求,轉發到其餘服務器上,進行處理,前端無感知

  nginx配置以下:

 1 server{
 2     listen 80;
 3     server_name a.com;
 4                                                                                                                      
 5     location /{
 6         proxy_pass http://127.0.0.1:8080/;
 7     }
 8                                                                                                                      
 9     location /test-ajax-cross/test{
# 也能夠轉發到其餘域名,進行跨域請求
10 proxy_pass http://localhost:8080/test-ajax-cross/test; 11 } 12 }

  一、編輯一個頁面test4.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     <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://a.com/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://a.com/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://a.com/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>

  二、配置nginx作反向代理

  三、使用地址 http://a.com/test-ajax-cross/static/test4.html#,進行訪問,測試是能正常訪問的

  Apache反向代理實現

  配置以下:

1 <VirtualHost *:80>
2     ServerName a.com
3     ErrorLog "logs/a.com-error.log"
4     CustomLog "logs/a.com-access.log" common
5     ProxyPass / http://127.0.0.1:8080/
6     ProxyPass /ajaxserverapache http://localhost:8080/test-ajax-cross/test
7 </VirtualHost>
相關文章
相關標籤/搜索