0.前言
CORS(Cross-Origin Resource Sharing)是一個用於處理跨域問題的W3C標準,本文將介紹什麼是跨域,引起跨域的同源策略,什麼是CORS,CORS的工作過程,請求方式以及與它有關的header,最後是java使用例子程式碼。
1.什麼是跨域?
瀏覽器從一個域名的網頁請求另一個域名的資源時,域名,埠,協議任意一個不同都是跨域請求。(需要注意域名和域名對應的ip也屬於跨域)
2.什麼是同源策略?
在Web瀏覽器中,允許某個網頁尾本訪問另一個網頁的資料,但前提是這兩個網頁必須有相同的協議,主機名和埠號,一旦兩個網站滿足上述條件,這兩個網站被認為同源。此策略可防止某個網頁上的惡意指令碼通過該網頁的文件物件模型訪問另一個網頁上的敏感資料,但是僅適用於指令碼,不適用於HTML標籤。
3.什麼是CORS?
CORS是一種基於HTTP 頭的機制,該機制通過允許伺服器標示除了它自己以外的其它origin(域,協議和埠),允許第一個資源所在的域之外的另一個域請求web頁面上的受限資源。尤其是Ajax請求,在預設情況下是被同源安全策略禁止的。CORS定義了一種瀏覽器和伺服器可以互動的方式,以確定允許跨源請求是否安全。它比純粹的同源請求允許更多的自由和功能,但比簡單的允許所有跨源請求更安全。整個CORS通訊過程,都是瀏覽器自動完成的,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加頭資訊,有時會多一次請求,但使用者不會有感覺,因此實現CORS通訊的關鍵在伺服器,只要伺服器實現了CORS介面,就可以進行跨源通訊。
4.CORS的工作過程
對於可以修改資料的Ajax和HTTP請求方法(通常為GET以外的HTTP方法或者用於某些MIME型別的POST使用),規範要求瀏覽器預先傳送請求,使用HTTP OPTIONS請求方法從伺服器請求支援的方法,在得到伺服器的“批准”後,使用實際的HTTP請求方法傳送實際請求。伺服器還可以通知客戶端“憑據”(包括Cookies和HTTP身份驗證資料)是否應隨請求一起傳送。
5.CORS請求
CORS請求分為簡單請求和預檢請求,上圖中傳送的真實請求為簡單請求,傳送真實請求前需進行的OPTIONS請求為複雜請求。
①簡單請求
概念:當請求方式為HEAD,GET或POST並且HTTP頭資訊僅包含Accept,Accept-Language,Content-Language,Last-Event-ID,Content-Type,其中Content-Type僅限三個值,包括application-x-www-form-urlencoded,multipart/form-data,text-plain。(這樣設計是為了相容form表單,因為表單一直可以傳送跨域請求。)
例子:加入使用者訪問http://www.example.com,並且跨域獲取http://service.example.com的使用者資訊。與CORS相容的瀏覽器將嘗試向service.example.com進行跨域請求,具體如下:
//瀏覽器新增一個額外的Origin頭,值為為父頁面提供服務的域,併傳送GET請求 Origin: http://www.example.com //在service.example.com域的服務將返回帶Access-Control-Allow-Origin頭,其值表示允許訪問的域或者返回錯誤頁面,表示伺服器不準跨域請求。 Access-Control-Allow-Origin: http://www.example.com Access-Control-Allow-Origin: * //萬用字元表示允許所有
[注:“*”的值很特殊,因為它不允許請求提供憑據,這意味著它不允許在跨域請求中傳送HTTP身份驗證,客戶端SSL證照或Cookie。在CORS體系結構中,Access-Control-Allow-Origin頭是由外部web服務設定的(service.example.com),而不是原始的web應用服務(www.example.com)設定的]
②預檢請求
概念:除了上面簡單請求的情況外都要先進行預檢請求,通過後再傳送真實請求。
例子:
//www.example.com域上的服務發出OPTIONS預檢請求 OPTIONS / Host: service.example.com Origin: http://www.example.com //如果service.example.com接受該操作,則響應如下: Access-Control-Allow-Origin: http://www.example.com Access-Control-Allow-Methods: PUT, DELETE //之後www.example.com將傳送真實請求,若不接受,將會對OPTIONS請求響應錯誤。
6.與CORS有關的頭
①請求頭
Origin
說明:該欄位必須,指示了請求來自於哪個站點。該欄位僅指示伺服器名稱,並不包含任何路徑資訊
語法:Origin: ""
Origin: <scheme> "://" <host> [ ":" <port> ]
示例:Origin: http://www.example.com
Access-Control-Request-Method
說明:該欄位必須,用於預檢請求告訴伺服器哪些HTTP方法在實際請求是將被使用
語法:Access-Control-Request-Method: <method>
示例:Access-Control-Request-Method: POST
Access-Control-Request-Headers
說明:用於預檢請求告訴伺服器在真正的請求中會採用哪些頭
語法:Access-Control-Request-Headers: <header-name>, <header-name>, ...
示例:Access-Control-Request-Headers: X-PINGOTHER, Content-Type
②響應頭
Access-Control-Allow-Origin
說明:該欄位必須,用於指定該響應是否被允許與給定的域共享
語法:Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: <origin>
示例:Access-Control-Allow-Origin:* --表示允許所有資源訪問
Access-Control-Allow-Origin:http://www.example.com --表示允許http://www.example.com訪問
Access-Control-Allow-Credentials
說明:該欄位可選。值為布林值,表示是否允許傳送Cookie,預設情況下,Cookie不包括在CORS請求之中。
語法:Access-Control-Allow-Credentials:true
Access-Control-Expose-Headers
說明:該欄位可選,用於列出哪些頭部可以作為響應的一部分暴露給外部。預設情況下有七種簡單響應頭部可以暴露給外部,包括:Cache-Control,Content-Language,Content-Length,Content-Type,Expires,Last-Modified,Pragma,Access-Control-Max-Age,Access-Control-Allow-Methods,Access-Control-Allow-Headers
語法:Access-Control-Expose-Headers: <header-name>, <header-name>, ...
示例:Access-Control-Expose-Headers: Content-Length
Access-Control-Max-Age
說明:該欄位可選,表示預檢請求的返回結果(即Access-Control-Allow-Method和Access-Control-Allow-Headers提供的資訊)可以被快取多久,即多久可以不用傳送預檢請求,若為-1則禁用,表示每次都請求都必須傳送預檢請求。
語法:Access-Control-Max-Age: <delta-seconds>
示例:Access-Control-Max-Age: 600
Access-Control-Allow-Methods
說明:該欄位必須,在對預檢請求的應答中明確了客戶端所要訪問的資源允許使用的方法或方法列表。
語法:Access-Control-Allow-Methods: <method>, <method>, ...
示例:Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers
說明:如果請求包括Access-Control-Request-Headers欄位,則該欄位必須,表明伺服器支援的所有header,不限於預檢中請求傳送的欄位還可能有自定義的。
語法:Access-Control-Allow-Headers: <header-name>[, <header-name>]*
Access-Control-Allow-Headers: *
示例:Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests
7.java操作
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; // 這裡填寫你允許進行跨域的主機ip httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); // 伺服器允許的header httpServletResponse.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); // 允許的訪問方法 httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH"); // Access-Control-Max-Age 用於 CORS 相關配置的快取 httpServletResponse.setHeader("Access-Control-Max-Age", "1209600"); // 是否允許傳送cookie httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; if (null != httpRequest && "OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) { return; } filterChain.doFilter(servletRequest, httpServletResponse); }