一 介绍
当HTTP请求一个域名、协议、端口任意之一与当前网页都不同的资源时,即为跨域请求。在浏览器中,为了安全,会限制脚本的跨域请求。CORS(Cross-Origin Resource Sharing)是一个通过HTTP头部实现脚本跨域的机制。
二 原理
当浏览器在域名A的网页内向域名B的资源发起跨域请求时,域名B的服务器会设置响应头部,告诉浏览器,该域资源允许什么域的跨域请求、允许什么头部、允许什么请求方法。如果该域的请求不被B域允许,浏览器则不会暴露响应给脚本代码,即浏览器其实收到了响应。
发起跨域请求时,也会带上CORS相关的请求头部,如Origin,表明跨域来源,等等
跨域请求的过程分两种:
- 当发起简单的跨域请求时(如GET请求或常用MIME类型的POST请求时),直接发起跨域请求。
- 当跨域请求复杂时(即非上述情况),会发起预请求(preflight,一个OPTIONS类型请求,也会带上CORS相应头部),查看B域支持的跨域请求。如果不被允许,则结束,否则发起真正的请求。
为什么跨域请求复杂时需要预请求?因为,即使跨域请求不被其他域允许,但该请求实际上是请求成功了,会对其他域服务器的数据造成副作用。因此先发送预请求,不被允许则直接结束。
默认情况下,跨域请求不会携带凭证(credentials,包含cookies和其他HTTP认证数据),并忽略响应的cookie设置。
但浏览器端可设置withCredentials
为true,允许跨域请求时携带凭证。此时服务端必须设置Access-Control-Allow-Credentials
字段,允许发起请求,并保存响应消息的cookie设置。
注意,携带的cookie和设置的cookie,都是被请求域相关的,因此不能在Devtool工具中的Application栏看到cookie,需要打开对应域名的网页才能看到被设置的cookie。
三 头部字段
这里介绍几个重要的响应头字段,并且一般都是对预请求响应的头字段:
Access-Control-Allow-Origin
:表示什么域可以请求该域,*
表示所有域都可请求该域。Access-Control-Allow-Headers
:实际请求可用的头字段Access-Control-Allow-Methods
:实际请求可用的请求方法Access-Control-Max-Age
:预请求响应结果被缓存的时间,-1
表示不缓存。Access-Control-Allow-Credentials
:表示是否允许跨域请求携带凭证。当浏览器的凭证模式为include
时,即请求携带凭证,该字段必须为true,此时服务端设置的cookie可以保存在浏览器上。注意,允许发送凭证时,响应头部Access-Control-Allow-Origin
不能为*
所有头字段,请见参考。
四 使用
下面来看看具体使用。后端使用spring MVC,使用注解@CrossOrigin
产生对应头字段,tomcat运行在80端口,部分代码如下:
@CrossOrigin(value = "http://127.0.0.1:63543",allowCredentials = "true")
@GetMapping("/test")
public Map<String,Object> test(HttpSession session,String name){
Map<String,Object> status=new HashMap<>();
String lastName= (String) session.getAttribute("name");
status.put("msg","you last record name:"+lastName);
session.setAttribute("name",name);
return status;
}
这里设置了,只允许http://127.0.0.1:63543
下的跨域请求,允许发送凭证。
下面在http://127.0.0.1:63543
下(这里使用Node做服务器)发送跨域请求,并携带cookie:
可以看到,也能跨域使用会话了。
如果你用DevTool查看cookie的话,你是查不到的,它被保存在了http://localhost
域下,详细见第二章。