JavaScript跨域后台拦截与CORS配置详解

作者:异步漫步

时间:2026-10-22

浏览:403

本文详解JavaScript跨域访问中服务端拦截器的配置方法。重点解析CORS机制,区分简单请求与非简单请求(含OPTIONS预检)。通过Java Filter示例,展示如何设置Access-Control-Allow-Origin及withCredentials处理Cookie共享,并验证跨域请求在服务端始终完整执行的特性。

文章目录

跨域后台拦截核心结论

本文主要介绍JavaScript跨域访问中,服务端如何通过后台设置进行拦截与处理。内容涉及跨域原理、配置方法及实际验证,供开发者参考学习。

在子域名相互访问的场景中,通常涉及跨域问题。

核心结论如下:

第一,服务端必须配置允许跨域的相关响应头。

第二,若客户端需携带Cookie,必须设置 withCredentials 属性。

第三,无论服务端是否允许跨域,该HTTP请求在服务端都会完整执行。

第四,对于非简单请求触发的 OPTIONS 预检请求,服务端需返回空响应或明确支持,否则若后续 requestMapping 不支持该方法,将导致错误。

环境搭建与Host配置

环境搭建

需求

首先需要构建两个运行环境:一个是提供API接口的Server A,另一个是需要跨域访问该API的Server B。

Server A提供了一个API接口。完整的请求URL如下:

https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759&_=1490855801818

Server B包含一个页面:

http://cros.corstest.com.net:3001/test.html

该页面需要请求Server A的API接口。

但由于浏览器的跨域保护机制,直接请求会失败:


No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'xxxxx' is therefore not allowed access.

修改Host配置

首先,在本地配置两个指向 127.0.0.1 的Host记录,以便进行跨域测试:


127.0.0.1 local.corstest.com.net 
127.0.0.1 cros.corstest.com.net

启动项目A,以提供API服务。

对于项目B,测试跨域只需编写一个HTML静态页面。因此创建一个 test.html,并通过工具发布:


 browser-sync 

安装


npm install -g browser-sync

本地启动一个test.html


browser-sync start --server --files "*.html" --host "cros.corstest.com.net" --port 3001

CORS机制与简单/非简单请求

关于跨域CORS

根据ruanyifeng的文章,浏览器将CORS请求分为两类:简单请求(simple request)和非简单请求(not-so-simple request)。

同时满足以下两个条件的即为简单跨域:

1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

其他情况属于非简单请求,这类请求对服务器有特殊要求,例如请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是 application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为“预检”请求(preflight),即 OPTIONS 请求。

跨域关键与Cookie处理验证

关键点

跨域的关键在于浏览器获得服务器的认可,而服务器的认可体现在响应头中的 Access-Control-Allow-Origin 。浏览器通过比较服务端返回的response中是否包含这个字段,以及该字段内容是否为当前网址,来确定是否允许跨域。也就是说,绕过浏览器是可以不用受跨域限制的。

这里有一个常被忽略的问题。

第一点,关于Cookie的传递。浏览器设置 withCredentials 为 true 时,会将Cookie发送给服务端。而服务端设置 Access-Control-Allow-Credentials 为 true 时接收Cookie,为 false 时不接收。关键在于,当请求到达Filter时才会决定是否设置response头,此时Cookie已经存在于request中。(待验证)

验证结果显示:服务端确实已经接受了Cookie,即使设置为 false,服务端仍旧接收Cookie。而客户端也仍旧可以发送Cookie。

第二点,在简单跨域中,浏览器的请求直接发送给服务器,服务器返回是否支持跨域(即是否在header中添加origin)。那么,简单跨域究竟请求了服务端几次?如果是1次,当服务端不支持跨域(未设置allow)时,是否还会继续执行请求并返回结果放入response?即不论跨域是否允许,服务器是否都会执行该request对应的计算。因为所有header设置都是告知浏览器的,与服务端限制无关。(待验证)

验证结果显示:即使服务端没有设置允许跨域,当客户端请求过来时,服务端仍旧完整执行了请求并返回结果,只是客户端没有接收。

服务端Filter配置与测试

服务端需要做点工作

针对上述两种跨域情况,Server A需要编写一个Filter。


<filter>
 <filter-name>cors</filter-name>
  <filter-class>com.test.filter.CorsFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>cors</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
</filter>

Filter配置:


public class CorsFilter extends OncePerRequestFilter {

 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
   throws ServletException, IOException {
  URL requestURL = new URL(request.getRequestURL().toString());
  String hostName = requestURL.getHost();
  String origin = request.getHeader("Origin");

  int index = hostName.indexOf(".");

  if(index > -1) {
   String domainHost = hostName.substring(index, hostName.length());
   if(!StringUtils.isEmpty(origin) && origin.contains(domainHost)) {
    response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    response.addHeader("Access-Control-Allow-Origin", origin);
    response.addHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.addHeader("Access-Control-Allow-Headers", "Content-Type, Cookie, " +
      "Accept-Encoding, User-Agent, " +
      "Host, Referer, " +
      "X-Requested-With, Accept, " +
      "Accept-Language, Cache-Control, Connection");

    if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
     // CORS "pre-flight" request
     response.setStatus(200);
     return;
    }
   }
  }

  filterChain.doFilter(request, response);
 }
}

上述Filter旨在允许同一domain下不同子域名跨域访问,而其他domain则不可以。因为我们需要共享Cookie,所以设置 Access-Control-Allow-Credentials 为 true 。如果设置为 false 则不接受Cookie。

客户端,即Server B如果想要发送Cookie,则需要设置 withCredentials 为 true 。


//原生
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
//jquery
$.ajax({
 ...
 xhrFields: {
  withCredentials: true
 }
 ...
});

注意:针对非简单跨域发送的 OPTIONS 请求,服务端A只需告知浏览器是否支持跨域即可,无需继续处理,否则若到达指定的requestMapping发现不支持该方法,将导致错误,因此直接返回即可。

下面针对简单跨域和非简单跨域进行测试:


<!DOCTYPE html>
<html lang="en">

 <meta charset="UTF-8">
 <title>test</title>

 <script src="jquery-1.11.3.js"></script>
</head>
<body>

<input type="button" value="GET_Default" onclick="testGetDefault()">
<input type="button" value="GET_JSON" onclick="testGetJSON()">
<input type="button" value="POST_Default" onclick="testPostDefault()">
<input type="button" value="POST_JSON" onclick="testPostJson()">
<input type="button" value="PUT" onclick="testPUT()">

<script>
 var getUrl = "https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759";
 var postUrl = "https://local.corstest.com.net:8443/contentmain/saveReservationDeposits.json?htid=759";

 function testGetDefault(){
  sendAjax("GET",getUrl, "json", "application/x-www-form-urlencoded");
 }
 function testGetJSON(){
  sendAjax("GET",getUrl, "json", "application/json; charset=utf-8");
 }
 function testPostDefault(){
  sendAjax("POST",postUrl, "json", "application/x-www-form-urlencoded");
 }

 function testPostJson(){
  sendAjax("POST",postUrl, "json", "application/json; charset=utf-8");
 }

 function testPUT(){
  sendAjax("PUT",postUrl, "json", "application/json; charset=utf-8");
 }

 
 function sendAjax(type, url, dataType, contentType){
  $.ajax( { 
   type: type,
   url: url,
   xhrFields: {
    withCredentials: true
   },
   dataType : dataType, // accept type
   contentType: contentType, //request type, default is application/x-www-form-urlencoded
   success: function(result){
    console.log(result);
   },
   error: function (xhr) {
    console.log(xhr);
   }
  });
 }


</script>
</body>
</html>

测试结果:

GET default:

只发送一个正常的GET请求。

GET json:

先发送一个OPTIONS预检请求如下:


General:
Request URL:https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759
Request Method:OPTIONS
Status Code:200 OK
Remote Address:127.0.0.1:8443

Response Headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, X-Requested-With, Accept, Accept-Language, Cache-Control, Connection
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:http://cros.corstest.com.net:3001
Content-Length:0
Date:Thu, 30 Mar 2017 12:47:44 GMT
Server:Apache-Coyote/1.1

Request Headers:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:GET
Connection:keep-alive
Host:local.corstest.com.net:8443
Origin:http://cros.corstest.com.net:3001
Referer:http://cros.corstest.com.net:3001/test.html
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

然后再发送正常的GET请求。

post default:

正常发送请求。

post json:先发送一个OPTIONS预检请求。然后再发送正常的请求。

其他情况同理,总之,非简单跨域会多发一次OPTIONS请求来确认是否支持跨域,此时服务端一定要返回支持跨域,并且直接返回即可。

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对本站的支持。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章