[Web] Access-Control-Allow-Origin 与 WithCredentials

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/buildcourage/article/details/84890911

Access-Control-Allow-Origin 与 WithCredentials

遇到的问题

在我们一个商城项目当中,前后端分离,前端使用Ajax(XMLHttpRequest),后台是Spring项目,需要实现跨域访问。

Access-Control-Allow-Origin

我之前从网上找到实现跨域的方法,在后台添加了如下Filter:

public class CrossDomainFilter extends OncePerRequestFilter {
	  @Override
	  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
			  throws ServletException, IOException {
		String referer = request.getHeader("referer");
		if (StringUtils.isNotBlank(referer)) {
		  URL url = new URL(referer);
		  String origin = url.getProtocol() + "://" + url.getHost();
		  if(url.getPort()!=-1){
			  origin+=":"+url.getPort();
		  }
		  response.addHeader("Access-Control-Allow-Origin", origin);
		} else {
		  response.addHeader("Access-Control-Allow-Origin", "*");
		}
	    response.addHeader("Access-Control-Allow-Credentials", "true");
	    response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
	    response.addHeader("Access-Control-Allow-Headers", "Content-Type");
	    filterChain.doFilter(request, response);
	  }
}

这样一般的跨域都没有问题了,但是当前端发起POST请求并向后台传Json格式数据时,浏览器控制台却报如下错误:

The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

而且我们注意到前端的这个请求如果不报错会发送两次,现在是第一次请求报了如上错误,第二次请求就没有发送了。

Preflight Request

经过查询,我们了解到浏览器发送的第一次请求是preflight request(预检验请求),用来获知服务器是否允许该跨域请求,如果允许,第二次才发送带数据的真实请求。

withCredentials

而通过查询上述的错误,我们也了解到前端需要和后台同步cookie,需要设置XMLHttpRequestwithCredentials属性为true,同时要求后台设置响应头Access-Control-Allow-Credentialstrue,并且响应头Access-Control-Allow-Origin不能为*,必须指定域名。

解决

显然,我的问题就在于响应头Access-Control-Allow-Origin*。通过检查代码和前端请求,我发现preflight request的请求头中没有Referer,所以我代码没有走到设置域名的分支,于是我改用了从请求头的Origin( 标识跨域资源请求)中获取请求源。修改后的代码如下:

public class CrossDomainFilter extends OncePerRequestFilter {
	  @Override
	  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
			  throws ServletException, IOException {
		String referer = request.getHeader("origin");
		if (StringUtils.isNotBlank(referer)) {
		  URL url = new URL(referer);
		  String origin = url.getProtocol() + "://" + url.getHost();
		  if(url.getPort()!=-1){
			  origin+=":"+url.getPort();
		  }
		  response.addHeader("Access-Control-Allow-Origin", origin);
		  response.addHeader("Access-Control-Allow-Credentials", "true");
		} else {
		  response.addHeader("Access-Control-Allow-Origin", "*");
		}
	    response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
	    response.addHeader("Access-Control-Allow-Headers", "Content-Type");
	    filterChain.doFilter(request, response);
	  }
}

参考资料

[1] Access-Control-Allow-Origin https://blog.csdn.net/blogdevteam/article/details/84874036
[2] preflight request https://www.jianshu.com/p/b55086cbd9af
[3] withCredentials https://www.jianshu.com/p/af1fc0fab4c5
[4] 前端必备HTTP技能之HTTP请求头响应头中常用字段详解 https://www.jianshu.com/p/6e86903d74f7
[5] 彻底搞清referrer和origin 彻底搞清referrer和origin https://blog.csdn.net/zdavb/article/details/51161130

猜你喜欢

转载自blog.csdn.net/buildcourage/article/details/84890911