ajax調取java服務器請求報錯javascript
報錯信息以下:html
'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. 前端
可是,請求狀態倒是成功:200,這是怎麼回事? java
ajax請求跨域:ajax出現請求跨域錯誤問題是由於瀏覽器的「同源策略」。web
同源策略:1995年,同源政策由 Netscape 公司引入瀏覽器。目前,全部瀏覽器都實行這個政策。ajax
所謂"同源"指的是"三個相同":協議相同&域名相同&端口相同,這三個要求必須一致,不然就叫跨域。json
同源政策的目的,是爲了保證用戶信息的安全,防止惡意的網站竊取數據(好比:同源策略能夠限制不一樣網站cookie共享的問題,經過cors設置照樣能夠實現cookie共享)。segmentfault
同源政策規定,AJAX請求只能發給同源的網址,不然就報錯。 跨域
方法一:JSONP瀏覽器
簡述:網頁經過添加一個<script>元素,向服務器請求JSON數據,這種作法不受同源政策限制;服務器收到請求後,將數據放在一個指定名字的回調函數裏傳回來。
可是,只能發送get請求,比較雞肋,其優點在於支持老式瀏覽器,以及能夠向不支持CORS的網站請求數據。
方法二:WebSocket
WebSocket是一種通訊協議,使用ws://(非加密)和wss://(加密)做爲協議前綴。該協議不實行同源政策,只要服務器支持,就能夠經過它進行跨源通訊。
方法三:CORS(推薦使用)
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。相比JSONP只能發GET請求,CORS支持全部類型的HTTP請求。
它容許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
CORS須要瀏覽器和服務器同時支持。目前,全部瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。
所以,實現CORS通訊的關鍵是服務器,只要服務器實現了CORS接口,就能夠跨源通訊。
java服務器設置
第一步:jar包配置
所需jar包:cors-filter-2.8.jar和java-property-utils-1.9.1.jar(這兩個庫文件放到對應項目的WEB-INF/lib/下)
若是是maven項目,將以下代碼添加到pom.xml中
<dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> <version>[ version CORS過濾器的最新的穩定版本 ]</version> </dependency>
第二步:添加CORS配置到項目的web.xml中( App/WEB-INF/web.xml)
<!-- 跨域配置CORS--> <filter> <!-- The CORS filter with parameters --> <filter-name>CORSFilter</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <!-- Note: All parameters are options, if omitted the CORS Filter will fall back to the respective default values. --> <!-- 是否容許http請求 --> <init-param> <param-name>cors.allowGenericHttpRequests</param-name> <param-value>true</param-value> </init-param> <!-- 容許跨域的域名(發送請求至該項目的地址、請求源) 構成(http://域名:端口號),好比(http://192.168.191.115:8080) --> <init-param> <param-name>cors.allowOrigin</param-name> <!-- *,表示:容許全部跨域請求,這樣的後果是:當須要客戶端請求攜帶cookie時,瀏覽器沒法攜帶cookie至服務器 能夠在servlet中動態設置:response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); 這,一樣是容許全部跨域請求,servlet的設置會覆蓋該屬性設置。
可是,當實際測試後發現,這裏的*永遠指向的是請求頭的Origin的值,因此不須要再進行額外的設置。 --> <param-value>*</param-value> </init-param> <!-- 容許子域 --> <init-param> <param-name>cors.allowSubdomains</param-name> <param-value>false</param-value> </init-param> <!-- 容許的請求方式(非簡單請求必須添加OPTIONS,由於"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。) --> <init-param> <param-name>cors.supportedMethods</param-name> <param-value>GET, HEAD, POST, OPTIONS,PUT</param-value> </init-param> <!-- 容許的請求頭參數,不能超出範圍 --> <init-param> <param-name>cors.supportedHeaders</param-name> <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value> </init-param> <!-- 自定義暴露本身的請求頭(自定義設置後Response Headers裏會顯示Access-Control-Expose-Headers及值) CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma; 若是想拿到其餘字段,就必須在Access-Control-Expose-Headers裏面指定。 --> <init-param> <param-name>cors.exposedHeaders</param-name> <!--這裏能夠添加一些本身的暴露Headers --> <param-value>X-Test-1, X-Test-2</param-value> </init-param> <!-- 容許客戶端給服務器發送cookie,若是不容許,刪除該屬性便可。(攜帶證書訪問) --> <init-param> <param-name>cors.supportsCredentials</param-name> <param-value>true</param-value> </init-param> <!-- 設定一次預檢請求的有效期,單位爲秒;該回應到期前不會再發出另外一條預檢請求。 --> <init-param> <param-name>cors.maxAge</param-name> <param-value>3600</param-value> </init-param> </filter> <!-- CORS Filter mapping --> <filter-mapping> <filter-name>CORSFilter</filter-name> <!-- 可自定義設置可供訪問的項目路徑 --> <url-pattern>/*</url-pattern> </filter-mapping>
注意:當web.xml文件中配置了多個filter時,須要將以上配置放在最前面,即:做爲第一個filter存在。
ajax代碼(html須要引入jQuery)
servlet處理
請求完成
沒有添加cors配置前
http的請求頭
ajax的請求頭
通過對比發現,咱們會發現兩點不一樣:
http請求不存在跨域問題,ajax出現跨域會報錯(跨域提醒且沒法實現數據交互),輸出在瀏覽器的控制檯上;
ajax請求會在Request Headers請求頭會增長一個頭部屬性:Origin,值爲當前網頁地址,形如:http://localhost:8070,可是,當瀏覽器檢測出該AJAX請求是跨域請求時,它的值會設置爲null。
Origin
字段用來講明:本次請求來自哪一個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否贊成此次請求。
若是Origin
指定的源,不在許可範圍內,服務器會返回一個正常的HTTP迴應。瀏覽器發現,這個迴應的頭信息沒有包含Access-Control-Allow-Origin
字段(詳見),就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest
的onerror
回調函數捕獲。
注意:這種錯誤沒法經過狀態碼識別,由於HTTP迴應的狀態碼有多是200(事實上,我們本次的狀態碼就是200)。
添加cors配置後
ajax能夠實現跨域請求,即:Origin
指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段。
Response Headers會多出如下字段:
Origin
字段的值,要麼是一個
*
,表示接受任意域名的請求。(必返回項)
注意:若是是非簡單請求,必返回的字段是Access-Control-Request-Method,。
小結:
第一,單純的http請求不存在跨域問題,ajax請求存在跨域問題。(而也是使用java發送http請求至服務器不存在跨域問題的根本緣由)
第二,跨域請求,請求頭會自動加上Origin字段;當服務器添加cors配置後,響應頭必返回Access-Control-Allow-Origin字段;
若是是非簡單請求可能會返回:Access-Control-Request-Method,表示:服務器端容許接受的http請求方式(當請求數據格式指定爲application/json時,並無出現該字段,按理說該出來的)。
咱們如今知道:協議、域名、端口三者只要有一個不一樣,就叫跨域。那麼,要重現跨域場景,就須要兩臺服務器,即同一臺電腦,跑兩個tomcat服務器便可(兩個tomcat的端口號必須不一樣,不然端口衝突)。
這裏只介紹最簡單的一種跨域請求方式:
將ajax代碼寫到一個單獨的html文件中,運行你的tomcat服務器, 使用瀏覽器打開該網頁文件,就可重現ajax跨域問題啦。
一旦服務器經過了"預檢"請求,之後每次瀏覽器正常的CORS請求,就都跟簡單請求同樣,會有一個Origin
頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin
頭信息字段。
如上圖所示,重現跨域問題後,按照第三步的cors方案,就能解決跨域問題啦。
錯誤示例:
本機運行了兩個項目對應兩個tomcat,端口號分別是8080和8070,
如上圖所示,Host表明的是服務端,Origin表明的是客戶端,二者的域名不一樣,其結果就是:
雖然能夠實現跨域,可是,沒法實現cookie共享(服務端照樣能夠返回cookie且瀏覽器能接收到,可是,瀏覽器發送請求至服務器時卻沒法攜帶cookie)。
因而可知,當域名徹底不一致時,cookie沒法實現跨域(子域不一樣的狀況我沒有進行測試)。
這讓我誤覺得,cookie跨域共享是沒法實現的,可是,事實並不是如此。
正確示例:
localhost:8080的服務器也能接收到實際的數據
打印結果
具體實現:
window.onload = function(){ // 前端往項目當中添加cookie document.cookie = "name=Marydon;path=/"; $.ajax({ url:'http://localhost:8080/test/crossServlet', method:'post', xhrFields: { withCredentials: true },// cookie可以傳過去的關鍵所在 /*crossDomain: true,*/ data:{name:'張三'}, success:function(data) { alert(data); } }); }
前端設置:關鍵就在於,ajax須要添加參數:xhrFields:{withCredentials:true};
後臺設置:(添加響應頭部信息設置,response.setHeader())
配置Access-Control-Allow-Credentials,值爲true,對應cors的cors.supportsCredentials;
配置Access-Control-Allow-Origin,值爲不能爲 '*',對應cors的cors.allowOrigin,但通過實踐發現,其值爲*時並無產生影響。
說明:
在前端添加cookie時,必須設置路徑,否則,cookie只做用於當前頁面;
在默認狀況下,只有設置 cookie的網頁才能讀取該 cookie。若是想讓一個頁面讀取另外一個頁面設置的cookie,必須設置cookie的路徑。
cookie的路徑用於設置可以讀取 cookie的頂級目錄。將這個路徑設置爲網站的根目錄(/),這樣全部網頁都能讀取到該cookie,
這也是js設置cookie後,後臺取不到值的緣由。
另外,在前端是獲取不到JSESSIONID這個cookie的,由於它設置了httpOnly屬性,即:只有後臺可以獲得該cookie。
小結:
cookie的跨域共享不是無條件的,即:請求和響應的IP徹底不相同時,沒法實現cookie共享,這就至關於A網站不能訪問B網站的cookie一個道理。
當請求發起方和接收方的域名(IP)徹底一致,端口號不一樣時,瀏覽器是能夠攜帶cookie的,也就是:服務器能接收到前端所傳來的cookie。
當IP的頂級域名相同時,沒有進行測試。
通過上述實踐發現:跨域cookie共享的侷限性很大,還不如不用,有實際使用場景的大佬,歡迎留言。