本文主要給大家介紹了關於Javascript跨域後台設置攔截的相關內容,分享出來供大家參考學習,話不多說了,來一起看看詳細的介紹吧。
子域名之間互相訪問需要跨域
結論放在開頭:
1.服務端必須設置允許跨域
2.客戶端帶cookie需要設置 withCredentials
3.無論服務端是否允許跨域,該request都會完整執行
4. options 預請求需要設置返回空,不然requestMapping沒有支持該方法則出錯
環境搭建
需求
首先需要搭建兩個環境。一個是提供API的server A,一個是需要跨域訪問API的server B。
Server A提供了一個api。完整的請求request是:
https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759&_=1490855801818
Server B有個頁面page:
http://cros.corstest.com.net:3001/test.html
並且這個page需要請求server A的api。
但由於跨域保護,請求失敗:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'xxxxx' is therefore not allowed access.
修改host
首先本地配置兩個指向127.0.0.1的host,方便互相跨域。
127.0.0.1 local.corstest.com.net 127.0.0.1 cros.corstest.com.net
啟動項目A,方便提供API。
至於項目B,測試跨域只要寫個html靜態頁面即可。那麼就寫一個test.html,並通過一個工具發布:
browser-sync
安裝
npm install -g browser-sync
本地啟動一個test.html
browser-sync start --server --files "*.html" --host "cros.corstest.com.net" --port 3001
關於跨域CORS
ruanyifeng 的文章裡說浏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
其中同時滿足一下2種標准即為簡單跨域:
1) 請求方法是以下三種方法之一:
2)HTTP的頭信息不超出以下幾種字段:
而其他情況,非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是 PUT 或 DELETE ,或者 Content-Type 字段的類型是 application/json 。非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight),即 options 請求。
關鍵
跨域的關鍵是浏覽器獲得服務器的認可,而服務器的認可就是header裡的 Access-Control-Allow-Origin 。浏覽器通過比較服務端返回的response中是否包含這個字段,以及包含這個字段的內容是否是當前網址來確定是否跨域。也就是說繞過浏覽器是可以不用跨域的。
有個問題,看好多文章並沒有指出。
第一點,帶cookie問題。浏覽器設置 withCredentials 為 true 則會帶cookie發送給服務端。而服務端設置 Access-Control-Allow-Credentials 為 true 則接收, false 則不接受。關鍵是到filter裡的時候才會決定是否設置response,那麼這時候cookie已經存在request裡了吧。(待驗證)
驗證:server端確實已經接受了cookie,即使設置為false,服務端仍舊接受cookie。而客戶端也仍舊可以發送cookie。
第二點,簡單跨域中,浏覽器的請求直接發送給服務器,服務器返回是否支持跨域(即是否header加origin), 那麼簡單跨域究竟是請求了服務端幾次?如果是1次,那麼如果服務端不支持跨域,即沒有設置allow,還會不會繼續走下去,會不會繼續request得到結果後放入response?就是不論跨域不跨域服務器是否都會執行這個request對應的計算。因為所有的設置header都是給浏覽器告知的,和服務端限制無關。(待驗證)
驗證:即使服務端沒有設置允許跨域,當客戶端請求過來時,服務端仍舊完整執行了請求並返回,只是客戶端沒有接收。
服務端需要做點工作
針對上述兩種跨域。server A需要寫一個filter。
<filter> <filter-name>cors</filter-name> <filter-class>com.test.filter.CorsFilter</filter-class> </filter> <filter-mapping> <filter-name>cors</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </filter>
Filter:
public class CorsFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { URL requestURL = new URL(request.getRequestURL().toString()); String hostName = requestURL.getHost(); String origin = request.getHeader("Origin"); int index = hostName.indexOf("."); if(index > -1) { String domainHost = hostName.substring(index, hostName.length()); if(!StringUtils.isEmpty(origin) && origin.contains(domainHost)) { response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); response.addHeader("Access-Control-Allow-Origin", origin); response.addHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Max-Age", "3600"); response.addHeader("Access-Control-Allow-Headers", "Content-Type, Cookie, " + "Accept-Encoding, User-Agent, " + "Host, Referer, " + "X-Requested-With, Accept, " + "Accept-Language, Cache-Control, Connection"); if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) { // CORS "pre-flight" request response.setStatus(200); return; } } } filterChain.doFilter(request, response); } }
上述filter是為了同一個domain下,不同子域名可以跨域訪問,而其他domain則不可以,因為我們需要共享cookie,所以設置 Access-Control-Allow-Credentials 為 true . 如果設置為 false 則不接受cookie。
客戶端,即server B如果想要發送cookie則需要設置 withCredentials 為 true .
//原生 var xhr = new XMLHttpRequest(); xhr.withCredentials = true; //jquery $.ajax({ ... xhrFields: { withCredentials: true } ... });
注意:針對非簡單跨域的時候發送 options 請求,服務端A需要告訴浏覽器是否支持跨域即可,不要往下走了,不然到指定的requestMapping發現不支持這個方法就會很尴尬了,所以直接返回。
下面針對簡單跨域和非簡單跨域做測試:
<!DOCTYPE html> <html lang="en"> <meta charset="UTF-8"> <title>test</title> <script src="jquery-1.11.3.js"></script> </head> <body> <input type="button" value="GET_Default" onclick="testGetDefault()"> <input type="button" value="GET_JSON" onclick="testGetJSON()"> <input type="button" value="POST_Default" onclick="testPostDefault()"> <input type="button" value="POST_JSON" onclick="testPostJson()"> <input type="button" value="PUT" onclick="testPUT()"> <script> var getUrl = "https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759"; var postUrl = "https://local.corstest.com.net:8443/contentmain/saveReservationDeposits.json?htid=759"; function testGetDefault(){ sendAjax("GET",getUrl, "json", "application/x-www-form-urlencoded"); } function testGetJSON(){ sendAjax("GET",getUrl, "json", "application/json; charset=utf-8"); } function testPostDefault(){ sendAjax("POST",postUrl, "json", "application/x-www-form-urlencoded"); } function testPostJson(){ sendAjax("POST",postUrl, "json", "application/json; charset=utf-8"); } function testPUT(){ sendAjax("PUT",postUrl, "json", "application/json; charset=utf-8"); } function sendAjax(type, url, dataType, contentType){ $.ajax( { type: type, url: url, xhrFields: { withCredentials: true }, dataType : dataType, // accept type contentType: contentType, //request type, default is application/x-www-form-urlencoded success: function(result){ console.log(result); }, error: function (xhr) { console.log(xhr); } }); } </script> </body> </html>
結果:
GET default:
只發送一個正常的get請求。
GET json:
先發送一個options如下:
General: Request URL:https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759 Request Method:OPTIONS Status Code:200 OK Remote Address:127.0.0.1:8443 Response Headers: Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, X-Requested-With, Accept, Accept-Language, Cache-Control, Connection Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Origin:http://cros.corstest.com.net:3001 Content-Length:0 Date:Thu, 30 Mar 2017 12:47:44 GMT Server:Apache-Coyote/1.1 Request Headers: Accept:*/* Accept-Encoding:gzip, deflate, sdch, br Accept-Language:zh-CN,zh;q=0.8 Access-Control-Request-Headers:content-type Access-Control-Request-Method:GET Connection:keep-alive Host:local.corstest.com.net:8443 Origin:http://cros.corstest.com.net:3001 Referer:http://cros.corstest.com.net:3001/test.html User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
然後再發送正常的Get請求。
post default:
正常發送請求。
post json: 先發送一個options請求。然後再發送正常的請求。
其他同理,總之,非簡單跨域會多發一次options請求來確認是否支持跨域,這時候服務端一定要返回支持跨域,並且直接返回即可。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對的支持。