package myggirl.wangpan.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 解决跨域问题
*/
@Configuration
public class FilterConfig {
@Bean
public RemoteIpFilter remoteIpFilter() {
return new RemoteIpFilter();
}
@Bean
public FilterRegistrationBean testFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());//添加过滤器
registration.addUrlPatterns("/*");//设置过滤路径,/*所有路径
registration.addInitParameter("name", "alue");//添加默认参数
registration.setName("MyFilter");//设置优先级
registration.setOrder(1);//设置优先级
return registration;
}
public class MyFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("初始化了");
}
@Override
public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain
filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) srequest;
HttpServletResponse response = (HttpServletResponse) sresponse;
String origin = request.getHeader("Origin");
String headers = request.getHeader("Access-Control-Request-Headers");
//支持跨域的域名,origin的值为跨域的域名。
if(!StringUtils.isEmpty(origin)) {
response.addHeader("Access-Control-Allow-Origin",origin);
}
//支持自定义头的跨域请求
if(!StringUtils.isEmpty(headers)) {
response.addHeader("Access-Control-Allow-Headers",headers);
}
//cookie跨域时不能用*。
//response.addHeader("Access-Control-Allow-Origin","*");
response.addHeader("Access-Control-Allow-Methods","*");
response.addHeader("Access-Control-Max-Age","3600");预检命令缓存时间,这段时间里不再发送域检命令。
//enable cookie
response.addHeader("Access-Control-Allow-Credentials","true");
//打印请求Url
System.out.println("过滤器过滤的,url :" + request.getRequestURI());
filterChain.doFilter(srequest, sresponse);
}
}
}
两种请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,请求方法是OPTIONS
,称为"预检"请求(preflight)。
非简单请求:比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json,或者加入自定义请求头。
预检请求
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错。
"预检"请求用的请求方法是OPTIONS
,表示这个请求是用来询问的。头信息里面,关键字段是Origin
,表示请求来自哪个源。
除了Origin
字段(请求头中会有一个origin字段,含义是发起请求的所在域名)。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
(3)Access-Control-Allow-Origin
该字段是一个逗号分隔的字符串,指定浏览器CORS请求允许的域名。
(4)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
(5)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true
,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true
,如果服务器不要浏览器发送Cookie,删除该字段即可。
withCredentials 属性
上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials
字段。
Access-Control-Allow-Credentials: true |
另一方面,开发者必须在AJAX请求中打开withCredentials
属性。
var xhr = new XMLHttpRequest(); |
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。