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

结论放在开头:

  1. 服务端必须设置允许跨域
  2. 客户端带cookie需要设置withCredentials
  3. 无论服务端是否允许跨域,该request都会完整执行
  4. options预请求需要设置返回空,不然requestMapping没有支持该方法则出错

环境搭建

需求

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

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

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

Server B有个页面page:

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

并且这个page需要请求server A的api。

但由于跨域保护,请求失败:

  1. 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,方便互相跨域。

  1. 127.0.0.1 local.corstest.com.net
  2. 127.0.0.1 cros.corstest.com.net

启动项目A,方便提供API。

至于项目B,测试跨域只要写个html静态页面即可。那么就写一个test.html,并通过一个工具发布:

browser-sync

安装

  1. npm install -g browser-sync

本地启动一个test.html

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

关于跨域CORS

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

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

  1. 1) 请求方法是以下三种方法之一:
  2. HEAD
  3. GET
  4. POST
  5. 2HTTP的头信息不超出以下几种字段:
  6. Accept
  7. Accept-Language
  8. Content-Language
  9. Last-Event-ID
  10. Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

而其他情况,非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUTDELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),即options请求。

关键

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

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

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

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

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

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

服务端需要做点工作

针对上述两种跨域。server A需要写一个filter。

  1. <filter>
  2. <filter-name>cors</filter-name>
  3. <filter-class>com.test.filter.CorsFilter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>cors</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>
  9. </filter>

Filter:

  1. public class CorsFilter extends OncePerRequestFilter {
  2. @Override
  3. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  4. throws ServletException, IOException {
  5. URL requestURL = new URL(request.getRequestURL().toString());
  6. String hostName = requestURL.getHost();
  7. String origin = request.getHeader("Origin");
  8. int index = hostName.indexOf(".");
  9. if(index > -1) {
  10. String domainHost = hostName.substring(index, hostName.length());
  11. if(!StringUtils.isEmpty(origin) && origin.contains(domainHost)) {
  12. response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  13. response.addHeader("Access-Control-Allow-Origin", origin);
  14. response.addHeader("Access-Control-Allow-Credentials", "true");
  15. response.setHeader("Access-Control-Max-Age", "3600");
  16. response.addHeader("Access-Control-Allow-Headers", "Content-Type, Cookie, " +
  17. "Accept-Encoding, User-Agent, " +
  18. "Host, Referer, " +
  19. "X-Requested-With, Accept, " +
  20. "Accept-Language, Cache-Control, Connection");
  21. if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
  22. // CORS "pre-flight" request
  23. response.setStatus(200);
  24. return;
  25. }
  26. }
  27. }
  28. filterChain.doFilter(request, response);
  29. }
  30. }

上述filter是为了同一个domain下,不同子域名可以跨域访问,而其他domain则不可以,因为我们需要共享cookie,所以设置Access-Control-Allow-Credentialstrue. 如果设置为false则不接受cookie。

客户端,即server B如果想要发送cookie则需要设置withCredentialstrue.

  1. //原生
  2. var xhr = new XMLHttpRequest();
  3. xhr.withCredentials = true;
  4. //jquery
  5. $.ajax({
  6. ...
  7. xhrFields: {
  8. withCredentials: true
  9. }
  10. ...
  11. });

注意,针对非简单跨域的时候发送options请求,服务端A需要告诉浏览器是否支持跨域即可,不要往下走了,不然到指定的requestMapping发现不支持这个方法就会很尴尬了,所以直接返回。

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

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <meta charset="UTF-8">
  4. <title>test</title>
  5. <script src="jquery-1.11.3.js"></script>
  6. </head>
  7. <body>
  8. <input type="button" value="GET_Default" onclick="testGetDefault()">
  9. <input type="button" value="GET_JSON" onclick="testGetJSON()">
  10. <input type="button" value="POST_Default" onclick="testPostDefault()">
  11. <input type="button" value="POST_JSON" onclick="testPostJson()">
  12. <input type="button" value="PUT" onclick="testPUT()">
  13. <script>
  14. var getUrl = "https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759";
  15. var postUrl = "https://local.corstest.com.net:8443/contentmain/saveReservationDeposits.json?htid=759";
  16. function testGetDefault(){
  17. sendAjax("GET",getUrl, "json", "application/x-www-form-urlencoded");
  18. }
  19. function testGetJSON(){
  20. sendAjax("GET",getUrl, "json", "application/json; charset=utf-8");
  21. }
  22. function testPostDefault(){
  23. sendAjax("POST",postUrl, "json", "application/x-www-form-urlencoded");
  24. }
  25. function testPostJson(){
  26. sendAjax("POST",postUrl, "json", "application/json; charset=utf-8");
  27. }
  28. function testPUT(){
  29. sendAjax("PUT",postUrl, "json", "application/json; charset=utf-8");
  30. }
  31. function sendAjax(type, url, dataType, contentType){
  32. $.ajax( {
  33. type: type,
  34. url: url,
  35. xhrFields: {
  36. withCredentials: true
  37. },
  38. dataType : dataType, // accept type
  39. contentType: contentType, //request type, default is application/x-www-form-urlencoded
  40. success: function(result){
  41. console.log(result);
  42. },
  43. error: function (xhr) {
  44. console.log(xhr);
  45. }
  46. });
  47. }
  48. </script>
  49. </body>
  50. </html>

结果:

GET default:

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

GET json:

先发送一个options如下:

  1. General:
  2. Request URL:https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759
  3. Request Method:OPTIONS
  4. Status Code:200 OK
  5. Remote Address:127.0.0.1:8443
  6. Response Headers:
  7. Access-Control-Allow-Credentials:true
  8. Access-Control-Allow-Headers:Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, X-Requested-With, Accept, Accept-Language, Cache-Control, Connection
  9. Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
  10. Access-Control-Allow-Origin:http://cros.corstest.com.net:3001
  11. Content-Length:0
  12. Date:Thu, 30 Mar 2017 12:47:44 GMT
  13. Server:Apache-Coyote/1.1
  14. Request Headers:
  15. Accept:*/*
  16. Accept-Encoding:gzip, deflate, sdch, br
  17. Accept-Language:zh-CN,zh;q=0.8
  18. Access-Control-Request-Headers:content-type
  19. Access-Control-Request-Method:GET
  20. Connection:keep-alive
  21. Host:local.corstest.com.net:8443
  22. Origin:http://cros.corstest.com.net:3001
  23. Referer:http://cros.corstest.com.net:3001/test.html
  24. 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请求来确认是否支持跨域,这时候服务端一定要返回支持跨域,并且直接返回即可。

Javascript跨域后台设置拦截的更多相关文章

  1. ajax 跨域 headers JavaScript ajax 跨域请求 +设置headers 实践

    解决跨域调用服务并设置headers 主要的解决方法需要通过服务器端设置响应头.正确响应options请求,正确设置 JavaScript端需要设置的headers信息 方能实现. 此处手札 供后人参 ...

  2. 【转】ajax 跨域 headers JavaScript ajax 跨域请求 +设置headers 实践

    解决跨域调用服务并设置headers 主要的解决方法需要通过服务器端设置响应头.正确响应options请求,正确设置 JavaScript端需要设置的headers信息 方能实现. 此处手札 供后人参 ...

  3. JavaScript 跨域漫游

    前言: 最近在公司做了几个项目都涉及到了iframe,也就是在这些iframe多次嵌套的项目中,我发现之前对iframe的认识还是比较不足的,所以就静下心来,好好整理总结了iframe的相关知识:&l ...

  4. 利用javascript跨域访问cookie之广告推广

    在上一篇<说一说javascript跨域和jsonp>中,利用JSONP进行了跨域的数据访问,利用JS本身的跨域能力在远端生成HTML结构的方式完成了一个小广告. 在实际应用中, 跨域使用 ...

  5. JavaScript跨域调用基于JSON的RESTful API

    1. 基本术语 AJAX(Asynchronous JavaScript And XML,异步JavaScript和XML):AJAX是一种用于创建快速动态网页的技术,通过在后台与服务器进行少量数据交 ...

  6. 优雅绝妙的Javascript跨域问题解决方案

    关于Javascript跨域问题的解决方案已在之前的一片文章中详细说明,详见:http://blog.csdn.net/sfdev/archive/2009/02/13/3887006.aspx: 除 ...

  7. JavaScript 跨域:window.postMessage 实现跨域通信

    JavaScript 跨域方式实现方式有很多,之前,一篇文章中提到了 JSONP 形式实现跨域.本文将介绍 HTML5 新增的 api 实现跨域:window.postMessage . 1 othe ...

  8. thinkphp,javascript跨域请求解决方案

    javascript跨域请求解决方案 前言 对于很多前端或者做混合开发的同学,我们难免会遇到跨域发起请求业务,比如A站点向B站点请求数据等等.由于最近要做一个站点集群的项目,所以具体业务要求很多个站点 ...

  9. JavaScript跨域解决方式

    平时工作中经常被JavaScript跨域问题所困扰,其实有很多种解决方式,下面给大家介绍常用的几种: 1.jsonp解决跨域问题 客户端代码: <!DOCTYPE html> <ht ...

随机推荐

  1. POJ 1845-Sumdiv 题解(数论,约数和公式,逆元,高中数学)

    题目描述 给定A,B,求A^B的所有因数的和,再MOD 9901 输入 一行两个整数 A 和 B. 输出 一行,一个整数 样例输入 2 3 样例输出 15 提示 对于100%的数据满足:0 <= ...

  2. AWS Organizations

    AWS Organizations offers policy-based management for multiple AWS accounts and is now generally avai ...

  3. ORA-00245问题总结

    (1)问题描述 在进行数据库归档备份时(备份归档日志文件和控制文件),有时成功,有时失败,失败报错如下: RMAN-00571: =================================== ...

  4. 一些java方面面试题,没事做做看看(带答案)

    1. Switch能否用string做参数? a.在?Java? <http://lib.csdn.net/base/java>7 之前, switch 只能支持byte,short,ch ...

  5. SOD开源框架MSF(消息服务框架)介绍

    前言:之前想做消息的广播,拖着就忘记了,现在拿了医生的框架来学习,就按实现了之前想实现的功能. 传送门http://www.cnblogs.com/bluedoctor/,框架的获取,按传送门的链接就 ...

  6. Centos6.5 源码编译安装 Mysql5.7.11及配置

    安装环境 Linux(CentOS6.5 版).boost_1_59_0.tar.gz.mysql-5.7.11.tar.gzMySQL 5.7主要特性:    更好的性能:对于多核CPU.固态硬盘. ...

  7. [leetcode-566-Reshape the Matrix]

    In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a new o ...

  8. python命令行神器Click

    原文: http://www.lengirl.com/code/python-click.html Click 是用Python写的一个第三方模块,用于快速创建命令行.我们知道,Python内置了一个 ...

  9. ASP.NET MVC Autofac依赖注入的一点小心得(包含特性注入)

    前言 IOC的重要性 大家都清楚..便利也都知道..新的ASP.NET Core也大量使用了这种手法.. 一直憋着没写ASP.NET Core的文章..还是怕误导大家.. 今天这篇也不是讲Core的 ...

  10. Egret index.html设置

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title> ...