一、概述
跨域,即CORS,全稱Cross-origin resource sharing, 跨域資源共享。是一個W3C的標準。
二、跨域所解決的問題
一般來說,瀏覽器具有同源策略,它限制了瀏覽器從一個源加載的文檔或腳本,不能與來自另一個源的資源進行交互,這是一種保護策略。
同源的定義:兩個源的 協議、域名、端口都相同,則他們是同源的。
同源策略控制了不同源之間的交互,這些交互通常分爲如下三類:
- 允許跨域寫操作:如重定向和表單提交等
- 允許跨域資源嵌入:如使用
<img>
嵌入圖片,使用<script>
嵌入腳本源文件等 - 不允許跨域讀操作:如使用js讀取另一個源的信息
解決跨域問題的方式,官方提供了標準,就是CORS。
三、具體方案
CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能。
整個CORS通信過程,都是由瀏覽器自動完成,不需要我們去參與。當瀏覽器檢測到跨域時,會自動添加一些消息用戶不會有感覺。因此,CORS的關鍵是服務器
四、 兩種請求
瀏覽器將CORS分爲兩類:簡單請求和非簡單請求
簡單請求
(1) 請求方法是以下三種方法之一:
- HEAD
- GET
- POST
(2)HTTP的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
滿足以上條件的是簡單請求,不滿足的就是非簡單請求。
簡單請求會自動在請求頭上加一個Origin字段,說明了協議、域名、端口
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
服務器根據該字段信息,決定是否同意該請求。如同意則返回消息頭會包含如下信息
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
第一個是必須的:表示能夠接受的域名請求
第二個是可選的:表示是否允許cookie發送
第三個是可選的:表示瀏覽器可以拿到的額外字段值。默認情況下CORS請求時只能拿到6個基本字段,這裏指定了額外的字段。
非簡單請求
非簡單請求是對服務器有特殊要求的請求,如傳輸JSON等。
在進行傳輸前,會先進行一次查詢請求,詢問服務器是否支持跨域,以及支持哪些HTTP方法和字段。稱爲preflight。只有當查詢通過後纔會發送正式的消息。
一個preflight請求頭如下,表示要發送PUT請求,並且額外發送的請求頭字段爲X-Custom-Header
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
響應如下,說明了允許的源,允許的方法,還有允許的請求頭字段。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面通過後,瀏覽器將會正式發送跨域請求,就像簡單請求的方式一樣。
除了上面提到的幾種返回字段外,還有一個字段
Access-Control-Max-Age: 1728000
它表示preflight的有效期,即本次preflight通過後,接下來的1728000秒內,該源不用再進行preflight操作即可直接發送請求了。
五、Spring對跨域的支持
局部跨域的支持
可以使用@CrossOrigin註解配合RequestMapping,針對某個特定的方法應用跨域設置,如下
@CrossOrigin(maxAge = 1800)
@RequestMapping(value = "/check")
@ResponseBody
public APIResult<User> findBoxList(HttpServletRequest request) {
全局跨域支持
可以在配置文件中,使用mvc:cors配置全局的跨域支持
<mvc:cors>
<mvc:mapping path="/**" allowed-origins="*" allow-credentials="true" max-age="1800" allowed-methods="GET,POST,DELETE,PUT,PATCH,OPTIONS"/>
</mvc:cors>
-
path: 指定適用的url
-
Allowed-origin: 允許的源
-
Allow-credentials: 允許發送cookie
-
Max-age: preflight的有效期
-
Allowed-methods: 允許的method
-
Allowed-headers: 允許的消息頭字段
-
Exposed-headers: 額外允許的消息頭字段
或者在實現了WebMvcConfigurer接口的配置類中重寫方法
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
/**
* 跨域配置
*/
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**")
.allowedOrigins("*").allowCredentials(true).maxAge(180)
.allowedMethods("GET","POST","DELETE","PUT","PATCH","OPTIONS");
}
... ...
}
CORS過濾器
爲了實現與其它不支持本地CORS的庫一起使用,Spring還提供了一個更加通用的跨域過濾器,CorsFilter,繼承該類,配置成過濾器即可工作。