欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Javascript实现跨域后台设置拦截的方法详解

程序员文章站 2022-11-25 12:37:32
本文主要给大家介绍了关于javascript跨域后台设置拦截的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍吧。 子域名之间互相访问需要跨域 结论放...

本文主要给大家介绍了关于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; wow64) applewebkit/537.36 (khtml, like gecko) chrome/56.0.2924.87 safari/537.36

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

post default:

正常发送请求。

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

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

总结

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