视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Javascript实现跨域后台设置拦截的方法详解
2020-11-27 22:33:19 责编:小采
文档


本文主要给大家介绍了关于Javascript跨域后台设置拦截的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍吧。

子域名之间互相访问需要跨域

结论放在开头:

1.服务端必须设置允许跨域

2.客户端带cookie需要设置 withCredentials

3.无论服务端是否允许跨域,该request都会完整执行

4. options 预请求需要设置返回空,不然requestMapping没有支持该方法则出错

环境搭建

需求

首先需要搭建两个环境。一个是提供API的server A,一个是需要跨域访问API的server B。

Server A提供了一个api。完整的请求request是:

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

Server B有个页面page:

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

并且这个page需要请求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

ruanyifeng 的文章里说浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

其中同时满足一下2种标准即为简单跨域:

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 请求。

    关键

    跨域的关键是浏览器获得服务器的认可,而服务器的认可就是header里的 Access-Control-Allow-Origin 。浏览器通过比较服务端返回的response中是否包含这个字段,以及包含这个字段的内容是否是当前网址来确定是否跨域。也就是说绕过浏览器是可以不用跨域的。

    有个问题,看好多文章并没有指出。

    第一点,带cookie问题。浏览器设置 withCredentials 为 true 则会带cookie发送给服务端。而服务端设置 Access-Control-Allow-Credentials 为 true 则接收, false 则不接受。关键是到filter里的时候才会决定是否设置response,那么这时候cookie已经存在request里了吧。(待验证)

    验证:server端确实已经接受了cookie,即使设置为false,服务端仍旧接受cookie。而客户端也仍旧可以发送cookie。

    第二点,简单跨域中,浏览器的请求直接发送给服务器,服务器返回是否支持跨域(即是否header加origin), 那么简单跨域究竟是请求了服务端几次?如果是1次,那么如果服务端不支持跨域,即没有设置allow,还会不会继续走下去,会不会继续request得到结果后放入response?就是不论跨域不跨域服务器是否都会执行这个request对应的计算。因为所有的设置header都是给浏览器告知的,和服务端无关。(待验证)

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

    服务端需要做点工作

    针对上述两种跨域。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; WOW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

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

    post default:

    正常发送请求。

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

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

    总结

    下载本文
    显示全文
    专题