W3C推荐的实现安全跨域请求的实现机制,CORS核心:让服务器决定是否允许跨域访问
使用场景
1、简单请求
1)只使用GET、HEAD或者POST请求方法,如果是POST,则数据类型(Content-Type)只能是application/x-www-form-urlencodeed、multipart/form-data、text/plain中的一种。
2)没有使用自定义的请求头(如x-token)
2、预请求
1)请求以GET、HEAD、POST之外的方法发起。或者,使用POST,但数据类型为application/x-www-form-urlencoded, multipart/form-data 或者 text/plain 以外的数据类型。
2)只使用服务端自定义的请求头(如xtoken)。
3、带凭证的请求
一般来说,对于跨站请求,浏览器是不会发送凭证(HTTP Cookies和验证信息)的。如果要发送带凭证的信息,只需要给XMLHttpRequest设置一个特殊的属性withCredentials = true,通过这种方式,浏览器就允许发送凭证信息。
带凭证的请求可能是简单请求,也可以是会有预请求。是否允许跨域,会先判断简单请求和预请求的规则,然后还会带上带凭证的请求自己的规则。
在带凭证的请求中,后端的响应必须包含HeaderAccess-Control-Allow-Credentials=true,同时Header Access-Control-Allow-Origin,不能再使用*号这种匹配符。
服务端响应的响应头
Access-Control-Allow-Origin: | * 允许的域名
Access-Control-Expose-Headers: 允许的白名单Header,多个用逗号隔开
Access-Control-Max-Age: 预请求缓存时间,单位秒
Access-Control-Allow-Credentials: true | false 是否允许带凭证的请求
Access-Control-Allow-Methods: 允许的请求类型,多个用逗号隔开
Access-Control-Allow-Headers: 在实际请求中,允许的自定义header,多个用逗号隔开
浏览器发出跨域请求头详解
Origin: 告诉服务器,请求来自哪里,仅仅是服务器名,不包含路径。
Access-Control-Request-Method: 预请求时,告诉服务器实际的请求方式
Access-Control-Request-Headers: 预请求时,告诉服务器,实际请求所携带的自定义Header
注意cors配置的Access-Control-Allow-Origin允许域名只是禁止配置之外域名获取服务端返回的数据,但是不阻止跨域请求的发送。
服务端Java实现示例:
/**
* @ClassName: CORSFilter
* @Description:
* @author:zyq
* @date 2018年03月08日
*
*/
public class CORSFilter implements Filter {
private final Set<String> ALLOWED_DOMAINS = new HashSet<String>() {
{
add(".ncf.wdtest.cc");
add(".weidai.com.cn");
add(".wd5.com.cn");
add(".wdai.com");
add(".wdgood.cn");
add(".weidai.work");
add(".wdtest.cc");
add(".wddev.cc");
}
};
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (allowCors(request)) {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
servletResponse.setHeader("Access-Control-Allow-Origin", servletRequest.getHeader("Origin"));//必填
servletResponse.setHeader("Access-Control-Allow-Methods", servletRequest.getHeader("Access-Control-Request-Method"));//可选
servletResponse.setHeader("Access-Control-Allow-Headers", servletRequest.getHeader("Access-Control-Request-Headers"));//可选
servletResponse.setHeader("Access-Control-Allow-Credentials", "true");//可选
servletResponse.setHeader("Access-Control-Max-Age", getCorsMaxAge(request));//可选,指定本次预检请求的有效期,单位为秒,我先写个1天
}
chain.doFilter(request, response);
}
//可继承改写,根据自己情况设置哪些源地址、目标url允许跨域
protected boolean allowCors(ServletRequest request) {
String originDomain = getHostName(((HttpServletRequest) request).getHeader("Origin"));
if (originDomain != null) {
for (String domain : ALLOWED_DOMAINS) {
if (originDomain.endsWith(domain)) {
return true;
}
}
}
return false;
}
private String getHostName(String url) {
if (url == null || url.length() == 0) {
return null;
}
try {
return new URL(url).getHost();
} catch (MalformedURLException e) {
return null;
}
}
//可继承改写,自己设置有效期
protected String getCorsMaxAge(ServletRequest request) {
return "86400";
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String allowedDomains = filterConfig.getInitParameter("allowedDomains");
if (allowedDomains != null) {
ALLOWED_DOMAINS.addAll(Arrays.asList(allowedDomains.split(",")));
}
}
}
浙公网安备 33010602011771号