跨域请求问题

背景

浏览器的同源策略与跨域
两个页面地址中的协议、域名和端口号一致,则表示同源。

同源策略的限制:

  • 存储在浏览器中的数据,如localStroage、Cooke和IndexedDB不能通过脚本跨域访问
  • 不能通过脚本操作不同域下的DOM
  • 不能通过ajax请求不同域的数据

何为ajax请求
AJAX只是一种技术,不是某种具体的东西。不同的浏览器有自己实现AJAX的组件。
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
用了AJAX之后,你点击,然后页面上的一行字就变化了,页面本身不用刷。

无关Cookie跨域Ajax请求

服务器端通过在响应的 header 中设置 Access-Control-Allow-Origin 及相关一系列参数,提供跨域访问的允许策略。

示例
1
2
3
4
5
6
//允许所有
response.setHeader("Access-Control-Allow-Origin", "*");
//允许特定
if(request.getHeader("Origin").contains("mydomain.com")) {
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
}

spring boot 通过配置文件实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 跨域请求配置
* 可以单独作为一个配置文件类,也可以直接放在spring boot的启动类中
*/
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
//允许跨域携带cookie
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}

@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}

带Cookie的跨域Ajax请求

如果这些GET或POST请求不需要传递Cookie数据的话,就没什么问题了,但如果需要,那么会发现 虽然已经处理了跨域请求的问题,但后台始终无法获取到Cookie。跨域传输Cookie是需要后台和前台同时做相关处理才能解决的。

前端代码
angularjs配置方法

angularjs
1
2
3
4
5
6
7
8
9
10
11
//单独配置
$http.post("http://a.domain.com/Api/Product", { productId: 3 }, {
withCredentials: true,
params: { name: "Ray" }
}).success(function (data) {
//TODO
});
//全局配置
angular.module("app").config(function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
})

jquery配置方法

jquery
1
2
3
4
5
6
7
8
9
10
11
12
13
$.ajax({
type: "POST",
url: "http://a.domain.com/Api/Product",
xhrFields: {
withCredentials: true
},
success: function (data) {
console.log(data)
},
error: function (data) {
console.error(data)
}
})

后端代码
对应客户端的 xhrFields.withCredentials: true 参数,服务器端通过在响应 header 中设置 Access-Control-Allow-Credentials = true 来运行客户端携带证书式访问。通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie。这里需要注意的是:

服务器端 Access-Control-Allow-Credentials = true时,参数Access-Control-Allow-Origin 的值不能为 ‘*’

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Spring Controller中的方法:
*/
@RequestMapping(value = "/corsrequest")
@ResponseBody
public Map<String, Object> getUserBaseInfo(HttpServletResponse response) {
if(request.getHeader("Origin").contains("woego.cn")) {
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
}
response.setHeader("Access-Control-Allow-Credentials", "true");
...
}

另一种方式,在一个没有response参数的全局代码中:

1
2
3
4
5
6
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getResponse();
//改写允许访问的域名地址为真实地址,解决跨域请求携带cookie问题
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

参考资料

Angularjs之如何在跨域请求中传输Cookie
跨域Ajax请求时是否带Cookie的设置