- 瀏覽器是先執行還是先判斷?
先執行后判斷
- 如何判斷?
跨域請求的請求頭中多了一個Origin字段,這個字段的值是當前的域名信息。也就說:瀏覽器發現本次請求是跨域請求時,就會在請求頭中增加一個Origin字段,然后等待服務器響應返回之后,判斷響應頭中有沒有允許Origin跨域的信息,如果有則OK,如果沒有則就會報錯。
在doFilter方法中加入以下代碼:
```
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "http://localhost:8080");
res.addHeader("Access-Control-Allow-Methods", "GET");
chain.doFilter(request, response);
```
以上代碼只允許一個域名的跨域,只允許GET請求,如果想允許多個域名跨域,多個請求方法,代碼修改為:
```
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "*");
```
*思考:Origin設置為*,是不是可以滿足所有的場景呢?*
# 簡單請求和非簡單請求
針對 “瀏覽器是先執行還是先判斷?”的問題,答案是:
瀏覽器發送跨域請求時,會判斷請求是否是簡單請求。**如果是簡單請求,則會先執行,后判斷;如果是非簡單請求,則會先判斷(發送一個預檢命令,檢查通過后,才會發送真正的請求),后請求**。
## 概念
### 簡單請求
1. request methods為以下幾種:
get head post
2. request header里面
1. 無自定義頭
2. Content-Type為以下幾種:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
### 非簡單請求
1. request methods為以下幾種:
put delete
2. request header里面
1. 有自定義頭
2. Content-Type為
- application/json
## OPTIONS預檢命令
request methods是OPTIONS。是在發送Content-Type=application/json形式的請求之前發送的一個命令。
發送OPTIONS請求時,瀏覽器會報如下錯誤:
```
Request header field Content-Type is not allowed by Access-Control-Headers in preflight response.
```
錯誤的意思是:發送OPTIONS請求時請求頭里面會有一個Access-Control-Request-Headers字段,字段值是content-type,但是響應頭中沒有允許。所以解決辦法就是在Filter中增加一個響應頭。
```
res.addHeader("Access-Control-Allow-Headers", "Content-Type");
```
## OPTIONS預檢命令的緩存
發送json形式的請求時,每次都會發送兩次請求:一次OPTIONS請求,一次真正的業務請求。這樣非常浪費資源,影響效率。解決問題的辦法是:可以增加一個響應頭,緩存預檢命令。
```
res.addHeader("Access-Control-Max-Age", "3600");
```
意思是:告訴瀏覽器,在3600秒之內緩存請求信息,不需要多次發送預檢命令
# 帶有Cookie的跨域
http中的回話,也就是session,是依賴于cookie來實現的,sessionId存放在cookie中
在之前的講解中提到:"Access-Control-Allow-Origin":"*"?這句代碼中的*能滿足所有的使用場景嗎?答案是:NO!NO!NO!
發送帶有cookie的跨域請求時:
1. Access-Control-Allow-Origin需要全匹配,不能是*號只能是本域
2. 需要添加`` res.addHeader("Access-Control-Allow-Credentials", "true")``
3. 前端以jquery為例
```
$.ajax({
type: 'get',
url: 'getCookie.do',
xhrFields: {
withCredentials: true
},
success (json) {
}
})
```
由于現在Origin是全匹配,那么也就是只能允許一個域名的跨域調用,如果其他域名也要可跨域調用,此時該如何解決?
解決辦法是:可以獲取到request headers中的Origin字段,然后動態的寫入Origin即可
```
String origin = req.getHeader("Origin");
if (!StringUtils.isEmpty(origin)) {
res.addHeader("Access-Control-Allow-Origin", origin);
}
```
# 帶自定義頭的跨域
前端:
```
$.ajax({
type: 'get',
url: 'getHeader.do',
headers: { // 添加自定義頭方法1
'x-header1': 'AAA',
},
beforeSend (xhr) { // 添加自定義頭方法2
xhr.setRequestHeader('x-header2', 'BBB')
},
success (json) {
}
})
```
后端:
```
String headers= req.getHeader("Access-Control-Allow-Headers");
if (!StringUtils.isEmpty(headers)) {
res.addHeader("Access-Control-Allow-Headers", headers );
}
```