跨域問題簡單的說就是前臺請求一個後臺連接,發送請求的前臺與後臺的地址不在同一個域下,就會產生跨域問題。這裏所指的域包括協議、IP地址、端口等。javascript
後端代碼:html
package cn.qs.controller; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test") @RestController public class TestController { @GetMapping("/get") public Map<String, Object> get(@RequestParam Map<String, Object> condition) { if (MapUtils.isEmpty(condition)) { condition = new LinkedHashMap<>(); condition.put("param", null); } return condition; } }
前端代碼:前端
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <script type="text/javascript" src="js/jquery-1.8.3.js" ></script> <body> </body> <script> + function test() { $.getJSON("http://localhost:8088/test/get.html", {}, function(res) { console.log(res); }); }(); </script> </html>
結果:雖而後端正常響應,可是JS報錯,這就是跨域安全問題,以下:java
js報錯以下:node
Failed to load http://localhost:8088/test/get.html: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8020' is therefore not allowed access.jquery
(1)瀏覽器限制nginx
發生ajax跨域的問題的時候後端是正常執行的,從後臺打印的日誌能夠看出,並且後臺也會正常返回數據。瀏覽器爲了安全進行了限制,說白了就是瀏覽器多管閒事。web
(2)跨域:ajax
當協議、域名、端口不一致瀏覽器就會認爲是跨域問題。spring
(3)XHR(XMLHttpRequest)請求,也就是ajax請求
若是不是ajax請求,不存在跨域問題(這個咱們應該能夠理解,瀏覽器直接訪問以及a標籤跳轉等方式都不會產生跨域問題)。
針對上面三個緣由能夠對跨域問題進行解決。思路以下:
(1)瀏覽器端:瀏覽器容許跨域請求,這個不太現實,咱們不可能改每一個客戶端
(2)XHR請求使用JSONP(JSON with Padding)方式進行方式。它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。
(3)針對跨域問題解決:
被調用方:也就是服務器端接口,服務器容許跨域。可是若是某些狀況服務器端不是咱們寫的就不可行了。
調用發:也就是JS客戶端,隱藏跨域。一般是經過代理的形式隱藏跨域請求,使請求都相似於同一域下發出a標籤。
好比chrom啓動的時候設置參數關閉安全檢查,以下:
chrome --disable-web-security --user-data-dir=g:/test
設置以後能夠正常進行訪問,這也進一步證實了跨域問題與後臺無關。
JSONP(JSON with Padding) 是一種變通的方式解決跨域問題。JSONP是一種非官方的協議,雙方進行約定一個請求的參數。該協議的一個要點就是容許用戶傳遞一個callback參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了。
JSONP發出的請求類型是script,不是XHR請求,因此能夠繞過瀏覽器的檢查。JSONP返回的是application/javascript,普通的xhr請求返回的是application/json。
JSONP的原理:經過向界面動態的添加script標籤來進行發送請求。script標籤會加上callback參數以及_,_是爲了防止請求被緩存。
好比咱們發送一個請求地址是http://localhost:8088/test/get.html?name=zhangsan&callback=handleCallback&_=123。後端看到有約定的參數callback,就認爲是JSONP請求,若是XHR正常請求的響應是{success: true},那麼後端會將回傳的JSON數據做爲參數,callback的值做爲方法名,如: handleCallback({success: true}), 並將響應頭的Content-Type設爲application/javascript,瀏覽器看到是JS響應,則會執行對應的handleCallback(data)方法。
(1)服務器端代碼須要改動
(2)只支持get方法,因爲JSONP原理是經過script標籤實現的,因此只能發送get請求
(3)不是XHR異步請求。因此不能使用XHR的一些特性,好比異步等。
後端:增長一個advice
package cn.qlq.aspect; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; @ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
前端:採用JSON包裝的JSONP請求
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <script type="text/javascript" src="js/jquery-1.8.3.js" ></script> <body> </body> <script> + function test() { $.ajax({ type : "get", async:false, url : "http://localhost:8088/test/get.html?name=zhangsan", dataType : "jsonp",//數據類型爲jsonp jsonp: "callback",//服務端用於接收callback調用的function名的參數 success : function(data){ console.log(data); }, error:function(){ alert('fail'); } }); }(); </script> </html>
結果:
(1)請求是script
請求頭:
(2)查看響應數據頭和數據:
數據以下:
/**/jQuery18309128178844464243_1575299406254({"name":"zhangsan","callback":"jQuery18309128178844464243_1575299406254","_":"1575299406287"});
補充:JSONP也能夠本身定義返回的方法名稱,默認是JSON生成的隨機字符串
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <script type="text/javascript" src="js/jquery-1.8.3.js" ></script> <body> </body> <script> var handleJSONPresponse = function (res) { console.log(1); console.log(res); console.log(2); } function test() { $.ajax({ type : "get", async:false, url : "http://localhost:8088/test/get.html?name=zhangsan", dataType : "jsonp",//數據類型爲jsonp jsonp: "callback",//服務端用於接收callback調用的function名的參數 jsonpCallback: "handleJSONPresponse", // callbacl的value值,不傳由jquery隨機生成 error:function(){ alert('fail'); } }); } test(); </script> </html>
查看請求數據:參數加_是爲了防止瀏覽器緩存JS請求
查看響應數據:
結果:
這裏所說的被調用方通常也就是指的是服務端。
客戶端發送請求到http服務器,一般是nginx/Apache;http服務器判斷是靜態請求仍是動態請求,靜態請求就直接響應,動態請求就轉發到應用服務器(Tomcat\weblogic\jetty等)。
固然也有省去中間靜態服務器的應用,就變爲客戶端直接請求應用服務器。
被調用方經過請求頭告訴瀏覽器本應用容許跨域調用。能夠從tomcat應用服務器響應請求頭,也能夠從中間服務器向請求頭添加請求頭。
(1)瀏覽器先執行仍是先判斷請求是XHR請求?
查看下面的簡單請求與非簡單請求的解釋。
(2)瀏覽器如何判斷?
分析普通請求和跨域請求的區別:
普通請求的請求頭以下:
XHR的請求以下:
能夠看出XHR請求的請求頭會多出一個Origin參數(也就是域),瀏覽器就是根據這個參數進行判斷的,瀏覽器會拿響應頭中容許的。若是不容許就產生跨域問題,會報錯。
補充:關於XHR請求頭中攜帶X-Requested-With與Origin
我本身測試,若是用jquery的ajax訪問本身站內請求是會攜帶X-Requested-With參數、不帶Origin參數,若是訪問跨域請求不會攜帶X-Requested-With參數,會攜帶Origin參數。
if ( !options.crossDomain && !headers["X-Requested-With"] ) { headers["X-Requested-With"] = "XMLHttpRequest"; }
1.被調用方過濾器中實現支持跨域
package cn.qs.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletResponse; /** * 容許跨域請求 */ @WebFilter(filterName = "corsFilter", urlPatterns = "/*") public class CorsFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse response2 = (HttpServletResponse) response; response2.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8020"); response2.setHeader("Access-Control-Allow-Methods", "GET"); chain.doFilter(request, response); } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } }
上面Access-Control-Allow-Origin是容許跨域請求的域, Access-Control-Allow-Methods 是容許的方法。
咱們再次查看XHR請求頭和響應頭:
若是容許全部的域和方法能夠用:
response2.setHeader("Access-Control-Allow-Origin", "*");
response2.setHeader("Access-Control-Allow-Methods", "*");
再次查看請求頭和響應頭:
這種跨域是不支持攜帶cookie發送請求的。
2.簡單請求和非簡單請求
簡單請求是先執行後判斷,非簡單請求是先發一個預檢命令,成功以後纔會發送請求。
(1)簡單請求:請求的方法爲GET\POST\HEAD方法中的一種;請求的header裏面無自定義頭,而且Content-Type爲:text/plain、multipart/form-data、application/x-www-form-urlencoded中的一種。
只有同時知足以上兩個條件時,纔是簡單請求,不然爲非簡單請求
(2)非簡單請求:put、delete方法的ajax請求;發送json格式的ajax請求;帶自定義頭的ajax請求。最多見的是發送json格式的ajax請求。非簡單會發送兩次請求:一個options的預檢請求、預檢請求根據響應頭判斷正確以後發送數據請求。
發送一個非簡單請求:
後端:
package cn.qs.controller; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test") @RestController public class TestController { @GetMapping("/get") public Map<String, Object> get(@RequestParam Map<String, Object> condition) { if (MapUtils.isEmpty(condition)) { condition = new LinkedHashMap<>(); condition.put("param", null); } return condition; } @PostMapping("/getJSON") public String getJSON(@RequestBody String param) { System.out.println(param); return param; } }
前端
function test() { $.ajax({ url: "http://localhost:8088/test/getJSON.html", type: "POST", data: JSON.stringify({name : "張三"}), contentType: "application/json;charset=utf-8", success: function(res) { console.log(res); } }); } test();
結果:(發送預檢請求的時候報錯)
控制檯報錯: (發送預檢的響應頭未設置須要的響應頭)
Failed to load http://localhost:8088/test/getJSON.html: Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
修改filter
package cn.qs.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletResponse; /** * 容許跨域請求 */ @WebFilter(filterName = "corsFilter", urlPatterns = "/*") public class CorsFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse response2 = (HttpServletResponse) response; // 容許請求的域(協議://IP:port) response2.setHeader("Access-Control-Allow-Origin", "*"); // 容許請求的方法 response2.setHeader("Access-Control-Allow-Methods", "*"); // 正確的響應預檢請求 response2.setHeader("Access-Control-Allow-Headers", "Content-Type"); chain.doFilter(request, response); } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } }
再次前端發送請求:(響應頭增長Access-Control-Allow-Headers預檢請求會正常響應,預檢成功以後會發送正常的數據請求,因此看到是發出兩個請求)
補充:預檢命令能夠緩存,過濾器向響應頭增長以下響應頭:(瀏覽器會緩存1個小時的預檢請求)
// 緩存預檢命令的時長,單位是s response2.setHeader("Access-Control-Max-Age", "3600");
1小時內發送非簡單請求只會預檢請求1次。咱們能夠用chrom的disable cache 禁掉緩存測試:
3.帶cookie的跨域請求
同域下發送ajax請求默認會攜帶cookie;不一樣域發送cookie須要進行設置,先後臺都須要設置。
(1)首先明白跨域請求須要後臺進行設置:請求頭的值 Access-Control-Allow-Origin 不能是*,必須是具體的域。須要根據請求頭的Origin獲取到請求的域以後寫到響應頭中。
(2)響應頭也須要增長容許攜帶cookie的字段 。
// 容許cookie response2.setHeader("Access-Control-Allow-Credentials", "true");
(3)客戶端發送ajax請求的時候須要withCredentials: true 容許攜帶cookie。A發ajax請求給B, 帶着的是B的cookie, 仍是受限於同源策略, ajax的Request URL是B, cookie就是B的
先在C:\Windows\System32\drivers\etc\hosts下面增長虛擬域名:
127.0.0.1 a.com
127.0.0.1 b.com
上面a.com 用於訪問靜態頁面,b.com 用於接收後端請求。
後端過濾器修改
package cn.qs.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; /** * 容許跨域請求 */ @WebFilter(filterName = "corsFilter", urlPatterns = "/*") public class CorsFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; // 容許訪問的源 String headerOrigin = request.getHeader("Origin"); if (StringUtils.isNotBlank(headerOrigin)) { response.setHeader("Access-Control-Allow-Origin", headerOrigin); } // 容許訪問的方法 response.setHeader("Access-Control-Allow-Methods", "*"); // 正確的響應預檢請求 response.setHeader("Access-Control-Allow-Headers", "Content-Type"); // 容許預檢命令緩存的時間 response.setHeader("Access-Control-Max-Age", "3600"); // 容許cookie response.setHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(request, response); } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } }
後端Controller:
@GetMapping("/getCookie") public String getCookie(@CookieValue(value = "cookie1", required = false) String cookie, HttpServletRequest request) { System.out.println("cookie: " + cookie); System.out.println("Origin: " + request.getHeader("Origin")); return cookie; } @GetMapping("/setCookie") public String setCookie(HttpServletRequest request, HttpServletResponse response) { Cookie cookie2 = new Cookie("cookie1", "value1"); cookie2.setPath("/"); response.addCookie(cookie2); String cookie = "cookie1=value1"; return cookie; }
前端JS:
function test() { $.ajax({ type : "get", async: false, url : "http://b.com:8088/test/getCookie.html", xhrFields: { withCredentials: true }, success: function(res) { console.log("res: " + res); }, error:function(){ alert('fail'); } }); } test();
測試:
(1)若是直接執行前端不會傳cookie,由於沒有cookie。以下:(因爲咱們訪問的服務是b.com域名,咱們的cookie須要是b.com域名下的cookie)
首先咱們訪問後臺 http://b.com:8088/test/setCookie.html 獲取cookie,固然能夠經過document.cookie進行設置
(2)接下來再訪問後臺:
請求頭以下:
響應頭以下:
(3)後臺控制檯日誌
cookie: value1
Origin: http://a.com:8020
4.帶自定義頭的跨域請求
過濾器修改,根據自定義請求頭在響應頭中增長容許的請求頭:
package cn.qs.filter; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; /** * 容許跨域請求 */ @WebFilter(filterName = "corsFilter", urlPatterns = "/*") public class CorsFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; // 容許訪問的源 String headerOrigin = request.getHeader("Origin"); if (StringUtils.isNotBlank(headerOrigin)) { response.setHeader("Access-Control-Allow-Origin", headerOrigin); } // 容許訪問的方法 response.setHeader("Access-Control-Allow-Methods", "*"); // 正確的響應預檢請求 // response.setHeader("Access-Control-Allow-Headers", "Content-Type"); // 容許自定義的請求頭(根據自定義請求頭) String headers = request.getHeader("Access-Control-Request-Headers"); if (StringUtils.isNotBlank(headers)) { response.addHeader("Access-Control-Allow-Headers", headers); } // 容許預檢命令緩存的時間 response.setHeader("Access-Control-Max-Age", "3600"); // 容許cookie response.setHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(request, response); } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } }
Controller:
@GetMapping("/getHeader") public JSONResultUtil<String> getHeader(@RequestHeader("x-header1") String header1, @RequestHeader("x-header2") String header2) { System.out.println(header1 + " " + header2); return new JSONResultUtil(true, header1 + " " + header2); }
前端:
<script> function test() { $.ajax({ url: "http://localhost:8088/test/getHeader.html", type: "get", headers: { "x-header1": "header1" }, beforeSend: function(xhr) { xhr.setRequestHeader("x-header2","header2"); }, xhrFields: { withCredentials: true }, success: function(res) { console.log(res); } }); } test(); </script>
結果:
咱們禁調緩存會發送兩條請求:
(1)預檢請求
(2)第二條請求
5. 被調用方解決-nginx解決方案(替代上面的filter的做用)
這裏用被調用方nginx解決是經過nginx代理以後增長所需的響應頭。
咱們仍是基於上面的配置的本地域名。 下面 a.com 用於訪問靜態頁面, b.com 用於接收後端請求。
127.0.0.1 a.com
127.0.0.1 b.com
(1)打開nginx/conf/nginx.conf,在最後的 } 前面增長以下:
include vhost/*.conf;
表示引入 當前目錄/vhost/ 下面全部後綴爲conf的文件。
接下來在當前conf目錄建立vhost目錄,並在下面建立b.com.conf文件,內容以下:
server { listen 80; server_name b.com; location /{ proxy_pass http://localhost:8088/; add_header Access-Control-Allow-Methods true; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Max-Age 3600; add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Headers $http_access_control_request_headers; if ($request_method = OPTIONS) { return 200; } } }
注意
(0)前面的是設置監聽域名是b.com、80端口,轉發到 http://localhost:8088/
(1)nginx中請求頭都是小寫,-要用_代替。
(2)$http_origin能夠取到請求頭的origin。
(3)最後判斷若是是預檢請求,會直接返回200狀態嗎。
關於nginx的使用:
nginx檢查語法:
E:\nginx\nginx-1.12.2>nginx.exe -t nginx: the configuration file E:\nginx\nginx-1.12.2/conf/nginx.conf syntax is ok nginx: configuration file E:\nginx\nginx-1.12.2/conf/nginx.conf test is successful
nginx從新加載配置文件:
nginx.exe -s reload
重啓和中止
nginx.exe -s reopen
nginx.exe -s stop
註釋掉filter以後修改前臺:異步訪問 b.com, 會被請求轉發到: http://localhost:8088/
function test() { $.ajax({ url: "http://b.com/test/getCookie.html", type: "get", headers: { "x-header1": "header1", "x-header3": "header3" }, beforeSend: function(xhr) { xhr.setRequestHeader("x-header2","header2"); }, xhrFields: { withCredentials: true }, success: function(res) { console.log(res); } }); } test();
(1)預檢命令
(2)第二次正式請求
6. Spring註解跨域:@CrossOrigin
加在類上表示全部方法容許跨域,加在方法表示方法中容許跨域。
package cn.qs.controller; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import cn.qs.utils.JSONResultUtil; @RequestMapping("/test") @RestController @CrossOrigin public class TestController { @GetMapping("/get") public Map<String, Object> get(@RequestParam Map<String, Object> condition) { if (MapUtils.isEmpty(condition)) { condition = new LinkedHashMap<>(); condition.put("param", null); } return condition; } @GetMapping("/getCookie") public String getCookie(@CookieValue(value = "cookie1") String cookie) { return cookie; } @PostMapping("/getJSON") public String getJSON(@RequestBody String param) { System.out.println(param); return param; } @GetMapping("/getHeader") public JSONResultUtil<String> getHeader(@RequestHeader("x-header1") String header1, @RequestHeader("x-header2") String header2) { System.out.println(header1 + " " + header2); return new JSONResultUtil(true, header1 + " " + header2); } }
被調用方解決跨域是經過nginx代理,將被調用方的請求代理出去,隱藏掉跨域請求。
(1)在nginx/conf/vhost下面新建a.com.conf,內容以下:
server { listen 80; server_name a.com; location /{ proxy_pass http://localhost:8020/; } location /server{ proxy_pass http://b.com:8088/; } }
解釋: 監聽 a.com 的80端口。 默認是/會轉發到本地的8020端口,也就是前臺頁面所用的端口;若是是/server/ 開始的會轉發到後端服務所用的路徑。
(2)Controller修改
@GetMapping("/getCookie") public String getCookie(@CookieValue(value = "cookie1", required = false) String cookie, HttpServletRequest request) { System.out.println("cookie1: " + cookie); System.out.println("====================="); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String header = (String) headerNames.nextElement(); String value = request.getHeader(header); System.out.println(header + "\t" + value); } return cookie; }
(3)前端修改:(統一訪問 /server 由nginx轉發到後端服務)
function test() { $.ajax({ url: "/server/test/getCookie.html", type: "get", headers: { "x-header1": "header1", "x-header3": "header3" }, beforeSend: function(xhr) { xhr.setRequestHeader("x-header2","header2"); }, xhrFields: { withCredentials: true }, success: function(res) { console.log(res); } }); } test();
(4)首先設置cookie:(cookie是設置爲a.com的cookie,nginx訪問轉發請求的時候也會攜帶到b.com)
查看cookie:
(5)刷新頁面測試:
前端查看:能夠看到前端請求發送至 a.com
請求頭:
響應頭:
後端控制檯:(能夠看到攜帶了x-requested-with參數,仍然是ajax請求,可是至關於同域請求。主機也是b.com(由nginx轉發過來的請求))
cookie1: a.com.cookie
=====================
host b.com:8088
connection close
pragma no-cache
cache-control no-cache
accept */*
x-header3 header3
x-requested-with XMLHttpRequest
x-header2 header2
user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
x-header1 header1
referer http://a.com/%E6%99%AE%E9%80%9A%E7%9A%84%E6%B5%8B%E8%AF%95/index.html?__hbt=1575599926569
accept-encoding gzip, deflate
accept-language zh-CN,zh;q=0.9
cookie cookie1=a.com.cookie
補充:調用方採用nodejs的express模塊和http-proxy-middleware進行代理
(1)安裝express模塊和http-proxy-middleware模塊:須要以管理員身份運行cmd
cnpm install --save-dev http-proxy-middleware cnpm install --save-dev express
(2)編寫nodejs代理腳本:
const express = require('express'); const proxy = require('http-proxy-middleware'); const app = express(); app.use( '/server', proxy({ target: 'http://b.com:8088', changeOrigin: true, pathRewrite: {'/server' : ''} })); app.use( '/', proxy({ target: 'http://a.com:8020' })); app.listen(80);
注意:上面的順序須要先代理/server,再代理/。不然會先匹配/。
(3)測試方法同上面nginx代理測試。
0.所謂的跨域請求是指XHR請求發送的時候 協議、域名、端口不徹底一致的狀況。只要有一個不一樣就是跨域。
1.若是用jquery的ajax訪問本身站內請求是會攜帶X-Requested-With參數、不帶Origin參數;若是訪問跨域請求不會攜帶X-Requested-With參數,會攜帶Origin參數。
2.後端獲取請求頭的時候不區分大小寫,好比說前端發送的請求頭是 x-header1:header1。後端能夠用 request.getHeader("X-HEADER1"); 接收。