跨域:不符合浏览器同源策略的请求不能执行,同源即域名、协议、端口都相同。这里主介绍CORS。
CORS两种请求:
- 简单请求 simple request
- 请求方法为HEAD、GET、POST之中任意一个
- 请求头只有以下字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-type (只限于用application/x-www-form-urlencoded、multipart/form-data、text/plain)
- 非简单请求 not-so-simple request
- 不满足简单请求条件都属于非简单请求
简单请求
简单请求
浏览器发出CORS请求,就是在头信息中增加一个Origin字段,用来说明此次请求是由那个源(协议、域名、端口)供服务器参考选择是否同意请求。如果服务器允许,会在响应头中添加如下字段:
Access-Control-Allow-Origin:
如果服务器允许某个源进行访问则该字段为必要字段,他的值一般与请求头中的Origin字段相同,设置成*代表允许所有请求,但存在安全问题,并且在要发送cookie的情况中不适用。
Access-Control-Allow-Credentials:
可选字段布尔类型,表示是否允许发送cookie。
Access-Control-Expose-Headers
可选字段,CORS请求中,XMLHTTPRequest对象getResponseHeader()方法只能拿到6个基本字段,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。若需要其他,则需要在这里指定。
客户端cookie问题:
CORS请求默认不发送cookie和http认证信息,如果要发送cookie,除了服务端认证之外:
Access-Control-Allow-Credentials: true
还需要请求打开withCredentials属性,js中:
var xhr = new XMLHttpRequest(); xht.withCredentials = true;
fetch中:
options.headers = { credentials: 'include', } fetch(url,options)
非简单请求
非简单CORS请求会在正式通信前先发送一个OPTIONS类型的预检(preflight)请求。服务器在响应头中添加一些字段通知浏览器是否可以请求,否则报错。
预检请求一般为OPTIONS请求,请求头中包含以下信息:
- Origin 请求源
- Access-Control-Request-Method
请求会使用哪些方法 - Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段
预检请求回应
Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token Access-Control-Allow-Methods:POST, GET, OPTIONS, DELETE Access-Control-Allow-Origin:http://demo.com.cn Access-Control-Max-Age:0 Allow:GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH Content-Length:0 Content-Type:text/html;charset=UTF-8 Date:Wed, 27 Dec 2017 00:18:29 GMT Server:Apache-Coyote/1.1 XDomainRequestAllowed:1
其中,关键字段为
Access-Control-Allow-Origin:...
表示同意了跨域请求。如果服务器拒绝了预检请求,则会没有该字段和相关字段。
java web配置允许CORS请求
java编写的服务端应用程序配置CORS方法很多,原理差不多,介绍一种简单实现:手动编写过滤器:
public class CorsFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
// TODO Auto-generated method stub
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest)request;
String origin = req.getHeader("Origin");
res.setContentType("text/html;charset=UTF-8");
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
res.setHeader("Access-Control-Max-Age", "0");
res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("XDomainRequestAllowed","1");
chain.doFilter(request, response);
}
public void destroy() {
// TODO Auto-generated method stub
}
}
在web.xml中注册过滤器:
<filter>
<filter-name>cors</filter-name>
<filter-class>com.mysoft.filter.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
fetch请求也要有一定修改:
fetch('http://172.0.0.1:8000/api/getAllUser', {
method: 'POST',
body: params,
mode:'cors',
headers: {
'Content-Type': 'application/json',
credentials: 'include',
Accept: 'application/json',
}
});
主要是添加mode:'cors'