Javascript跨域后台设置拦截
子域名之间互相访问需要跨域
结论放在开头:
- 服务端必须设置允许跨域
- 客户端带cookie需要设置
withCredentials - 无论服务端是否允许跨域,该request都会完整执行
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,并通过一个工具发布:
安装
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请求来确认是否支持跨域,这时候服务端一定要返回支持跨域,并且直接返回即可。
Javascript跨域后台设置拦截的更多相关文章
- ajax 跨域 headers JavaScript ajax 跨域请求 +设置headers 实践
解决跨域调用服务并设置headers 主要的解决方法需要通过服务器端设置响应头.正确响应options请求,正确设置 JavaScript端需要设置的headers信息 方能实现. 此处手札 供后人参 ...
- 【转】ajax 跨域 headers JavaScript ajax 跨域请求 +设置headers 实践
解决跨域调用服务并设置headers 主要的解决方法需要通过服务器端设置响应头.正确响应options请求,正确设置 JavaScript端需要设置的headers信息 方能实现. 此处手札 供后人参 ...
- JavaScript 跨域漫游
前言: 最近在公司做了几个项目都涉及到了iframe,也就是在这些iframe多次嵌套的项目中,我发现之前对iframe的认识还是比较不足的,所以就静下心来,好好整理总结了iframe的相关知识:&l ...
- 利用javascript跨域访问cookie之广告推广
在上一篇<说一说javascript跨域和jsonp>中,利用JSONP进行了跨域的数据访问,利用JS本身的跨域能力在远端生成HTML结构的方式完成了一个小广告. 在实际应用中, 跨域使用 ...
- JavaScript跨域调用基于JSON的RESTful API
1. 基本术语 AJAX(Asynchronous JavaScript And XML,异步JavaScript和XML):AJAX是一种用于创建快速动态网页的技术,通过在后台与服务器进行少量数据交 ...
- 优雅绝妙的Javascript跨域问题解决方案
关于Javascript跨域问题的解决方案已在之前的一片文章中详细说明,详见:http://blog.csdn.net/sfdev/archive/2009/02/13/3887006.aspx: 除 ...
- JavaScript 跨域:window.postMessage 实现跨域通信
JavaScript 跨域方式实现方式有很多,之前,一篇文章中提到了 JSONP 形式实现跨域.本文将介绍 HTML5 新增的 api 实现跨域:window.postMessage . 1 othe ...
- thinkphp,javascript跨域请求解决方案
javascript跨域请求解决方案 前言 对于很多前端或者做混合开发的同学,我们难免会遇到跨域发起请求业务,比如A站点向B站点请求数据等等.由于最近要做一个站点集群的项目,所以具体业务要求很多个站点 ...
- JavaScript跨域解决方式
平时工作中经常被JavaScript跨域问题所困扰,其实有很多种解决方式,下面给大家介绍常用的几种: 1.jsonp解决跨域问题 客户端代码: <!DOCTYPE html> <ht ...
随机推荐
- 实现最简单PHP MVC实例
关于网上大多MVC的简介我就不再多说,就是Model(模型)View(视图) C(控制器)这里作为一个刚入门PHP MVC框架的我,这里我搭建一个最简易的mvc项目,从而理解MVC 1在apache服 ...
- 自动清理SQLServerErrorLog错误日志避免太大
问题描述:开启SQLServer自动备份后,备份文件越来越多,有没有及时清理,导致服务器空间不足,备份出错,以至于出现几个G的ErrorLog文件,影响系统的登录管理. 解决办法:定期清理SQLSer ...
- SICP-2.2-数据的抽象
数据的抽象 生活中有许多的事物具有复合结构,例如地理位置所用的经纬度,便是通过一个复合结构来代表位置,在我们的程序当中,我们设法将经度纬度组合成一对,我们既可以把他们当做一个整体单元来进行操作,而且也 ...
- git的使用[转]
本节内容 github介绍 安装 仓库创建& 提交代码 代码回滚 工作区和暂存区 撤销修改 删除操作 远程仓库 分支管理 多人协作 github使用 忽略特殊文件.gitignore 为什么要 ...
- 使用CodeDOM动态编译一个字符串表达式
由于程序需要,计算的表达式使用字符串传输,这样对运算造成了影响.在程序中直接执行这段表达式可以得到值, 但是使用字符串就没有办法运算了, 所以想到用CodeDOM将这段字符串拼接在代码中编译 类似st ...
- AutoMapper 6.x 扩展方法
简介 很多时候我们使用AutoMapper的时候,都需要进行一个配置才可以使用Mapper.Map<Source,Target>(entity);.如果不进行配置则会报错. 如果实体过多, ...
- Linux内核互斥锁--mutex
一.定义: /linux/include/linux/mutex.h 二.作用及访问规则: 互斥锁主要用于实现内核中的互斥访问功能.内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见 ...
- 数据结构(C语言版)链表相关操作算法的代码实现
这次实现的是带头结点的单链表的初始化.遍历.创建.插入.删除.判断链表是否为空.求链表长度函数,编译环境是vs2013. 其中插入和删除函数中循环的条件目前还不太明白. #include<ios ...
- NewsDao
package com.pb.news.dao; import java.util.Date;import java.util.List; import com.pb.news.entity.News ...
- Chrome浏览器扩展开发系列之十一:NPAPI插件的使用
在Chrome浏览器扩展中使用HTML和JavaScript非常容易,但是如何重用已有的非JavaScript遗留系统代码呢?答案是将NPAPI插件绑定到Chrome浏览器扩展,从而实现在Chrome ...