- 浏览器是先执行还是先判断?
先执行后判断
- 如何判断?
跨域请求的请求头中多了一个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 );
}
```