跨域ajax请求中的cookie传输问题
背景
在项目(域名为a.cn)中,做单点登录时,请求b.cn域名进行用户名密码登录。项目中本域下(a.cn)以jsonp方式跨域请求时,jsonp只支持get请求,用户密码等敏感信息会被明文传输到ngnix日志上,存在泄漏的安全风险,因此采用非jsonp的方式进行跨站访问。
在b.cn服务中添加response header:
("Access-Control-Allow-Origin": "https://www.a.cn")
知识点
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
对CORS协议不了解的同学,可以点击这里
服务端引用Access-Control-Allow-Origin后带来了新的问题,b.cn域下的cookie无法传输到a.cn的域下。
下面我们就一起讨论下其中的cookie传输问题。
场景
http://a.cn/test.html向
http://b.cn/test/cors
发起ajax请求。
b.cn/test/cors种cookie(name:xudj)
test.html第二次发起请求时,希望将cookie(name:xudj)带给b.cn/test/cors。
前提
为了模拟简单一点,因为不同端口也是属于跨域请求,所以:
使用http://localhost:8182/test.html 代替http://a.cn/test.html 下面称其(客户端)
使用http://localhost:8080/load/data/cors 代替http://b.cn/test/cors 下面称其(服务端)
问题复现
localhost:8182客户端代码如下:
http://localhost:8182/test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>项目网站</title>
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script>
var url = "http://localhost:8080/load/data/cors";
$.ajax({
url:url,
type:"GET",
success:function(res){
console.log(res);
}
})
</script>
</body>
</html>
localhost:8080服务端代码如下
http://localhost:8080/load/data/cors
@RequestMapping(value = "/load/data/cors", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public void loadData3(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 指定允许域名访问
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8182");
// 是否允许跨域请求携带认证信息(cookies等)
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Headers", "Origin, x-requested-with, Content-Type, Accept,X-Cookie");
response.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE");
Cookie[] cookies = request.getCookies();
String name = "";
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("name".equals(cookie.getName())) {
name = cookie.getValue();
break;
}
}
}
if (name == null || name.equals("")) {
// 设置cookies
Cookie cookie = new Cookie("name", "xudj");
cookie.setPath("/");
// 注意,如果response.addHeader("Access-Control-Allow-Origin", "*");配置为*是无法携带cookie的。
response.addCookie(cookie);
}
System.out.println("name:" + name);
response.getWriter().write("{\"success\", \"true\"}");
}
且多次刷新,服务端也获取不到对应cookie。
解决
localhost:8182客户端代码如下:
http://localhost:8182/test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>项目网站</title>
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script>
var url = "http://localhost:8080/load/data/cors";
$.ajax({
url:url,
type:"GET",
xhrFields:{
withCredentials:true
},
success:function(res){
console.log(res);
}
})
</script>
</body>
</html>
withCredentials:true 是关键。只有加上此选项,浏览器才会跨域携带cookie。否则,即使服务器同意发送Cookie,浏览器请求也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
localhost:8080服务端代码如下(不变)
http://localhost:8080/load/data/cors
@RequestMapping(value = "/load/data/cors", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public void loadData3(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 指定允许域名访问
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8182");
// 是否允许跨域请求携带认证信息(cookies等)
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Headers", "Origin, x-requested-with, Content-Type, Accept,X-Cookie");
response.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE");
Cookie[] cookies = request.getCookies();
String name = "";
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("name".equals(cookie.getName())) {
name = cookie.getValue();
break;
}
}
}
if (name == null || name.equals("")) {
// 设置cookies
Cookie cookie = new Cookie("name", "xudj");
cookie.setPath("/");
// 注意,如果response.addHeader("Access-Control-Allow-Origin", "*");配置为*是无法携带cookie的。
response.addCookie(cookie);
}
System.out.println("name:" + name);
response.getWriter().write("{\"success\", \"true\"}");
}
说明:
-
Access-Control-Allow-Credentials: true
与浏览器侧的withCredentials:true成对使用,表明许可服务端发cookie,且客户端会写入cookie。 -
Access-Control-Allow-Origin: http://localhost:8182
表示服务端接收客户端的请求。如果请求时不需要带cookie,此字段可以写*,表明该站接收所有来源的ajax请求。如果需要传输cookie, 该字段只能写一个固定来源。
访问test.html,第二次时如愿在localhost:8080/load/data/cors
服务端接口下看到控制台输入:name:xudj
如图:
这说明:
1、localhost:8080服务端成功将cookie携带并写到了localhost:8182客户端域所在的网站。
2、localhost:8080服务端成功从localhost:8182客户端携带了cookie到其服务器。
3、跨域携带cookie服务端的Access-Control-Allow-Origin必须指定确定域名。报错如下: