跨域問題說明,參考【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
一、編輯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請求,能夠看到已經能正常返回
一、編輯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 }
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配置。
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 }
<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請求,轉發到其餘服務器上,進行處理,前端無感知
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#,進行訪問,測試是能正常訪問的
配置以下:
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>