# AJAX 和跨域
# 1. 跨域问题原由
问题
什么是 ajax 跨域问题?
简单来说,就是前台调用后台接口的时候,由于『浏览器』的同源策略(Same-origin Policy)过于严格,如果这个接口不是同一个域的,就会产生跨域问题。
问题
什么是同源请求,什么是跨域请求?
所谓同源是指:域名、协议、端口相同。
如果发出去的请求不是本域的,协议、域名、端口,任何一个不一样,浏览器就认为是跨域的。
URL | 说明 | 是否跨域 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js | 同一域名 | 没有跨域 |
http://www.a.com/a/a.js http://www.a.com/b/b.js | 同一域名下的不同文件夹 | 没有跨域 |
http://www.a.com/a.js http://www.a.com :8080 /b.js | 端口不同 | 跨域 |
http: //www.a.com/a.js https: //www.a.com/b.js | 协议不同 | 跨域 |
http://www.a.com /a.js http:// 70.32.97.74 /b.js | 域名和域名的 IP | 跨域 |
http://www .a.com/a.js http:// xxx .a.com/a.js http:// yyy .a.com/b.js | 二级域名不同 | 跨域(此时,cookie 也不会共享) |
http://www.a.com /a.js http:// www.b.com /b.js | 域名不同 | 跨域 |
需要注意的是:发送的是 xhr(XMLHTTPRequest)请求才会产生跨域问题。如果发出去的请求不是 XHR 请求的话,即使跨域,浏览器也不会报错。
# 2. CORS 之前的跨域问题的解决办法
# 方案一:关闭浏览器的跨域限制功能
这是最简单粗暴,也是最不可能采用的方案。
# 方案二:JSONP(JSON with Padding)
JSONP 方案是一个早期方案,也是一个非标准方案。不建议使用。
它的解题思路是,既然只有发送的是 xhr(XMLHTTPRequest)请求才会有可能产生跨域问题(请求被浏览器拦截/阻止),那么我们就想办法 把 XHR 请求改为非 XHR 请求 。只要不是 XHR 请求,浏览器就不会报跨域预警,就会放行请求。
由于该方案是一个早期的非标准方案,且存在若干缺点,因此慢慢被后续的 CORS
所取代。此处不作讲解。
# 3. 现行的标准的方案:CORS
CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。
简单来说,就是浏览器『一刀切式地阻止一切跨域请求』过于的粗暴,因此,CORS 的方案就是『让服务器决定是否响应这个跨域请求』。服务器不响应,浏览器就阻止显示响应结果;服务器愿意响应,浏览器就放行,展示响应结果。
TIP
整个 CORS 通信过程,都是浏览器自动完成(基本上目前所有的浏览器都实现了 CORS 标准),不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但『整个过程用户不会有感觉』。
浏览器首先会发起一个请求方法为 OPTIONS 的『预检请求』,用于询问/确认浏服务器是否允许跨域请求,只有在得到服务器的许可后才会发出实际请求。
# 4. Spring Boot 对 CORS 的支持
# 环境准备
首先是支持 Restful 的 Controller,这里就不使用数据库了,简单一点。
点击查看代码 UserController
@RestController
public class UserController {
/**
* 查询用户列表
*/
@GetMapping(value = "/users")
public ResponseEntity<Map<String, Object>> getUserList() {
Map<String, Object> map = new HashMap<>();
List<User> userList = new ArrayList<>();
userList.add(new User(1, "tommy", 20));
userList.add(new User(2, "jerry", 19));
map.put("result", userList);
map.put("status", 200);
return ResponseEntity.ok(map);
}
}
点击查看代码 User
public class User {
private int id;
private String username;
private int age;
// getter / setter
}
点击查看代码 页面
不是使用 IDEA 的用户,自己找个 tomcat 启动 test.html
页面,修改端口为其他。是 IDEA 的用户在 test.html
页面源码上鼠标右键选择某个浏览器打开(这里用到了 IDEA 内置服务器的功能)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/2.1.0/jquery.min.js"></script>
<script type="text/javascript">
function crosRequest(){
$.ajax({
url:'http://localhost:8080/users',
type: 'get',
dataType: 'json',
success: function(data){
console.log(data);
}
});
}
</script>
</head>
<body>
<button onclick="crosRequest()">请求跨域资源</button>
</body>
</html>
# CORS 配置方案一:@CrossOrigin 注解
在请求处理方法,或者是在 Controller 类上标注 @CrossOrigin
注解,表示本方法,或本类中的所有方法接收来自【远方】的跨域请求。
@CrossOrigin
@GetMapping(value = "/users")
public ResponseEntity<Map<String, Object>> getUserList() {
...
}
@CrossOrigin
注解的属性的默认值是:
"Access-Control-Allow-Origin" : "*"
"Access-Control-Allow-Methods" : "GET,POST,PUT,OPTIONS"
"Access-Control-Allow-Credentials" : "true"
如果有需要的话,你可以通过注解的属性进行手动指定。例如:
@CrossOrigin(
origins = { "http://domain.com", "http://domain2.com", "http://localhost:63342" },
methods = { RequestMethod.GET, RequestMethod.POST },
allowCredentials = "true")
# 方案二:全局 CORS 配置
如果使用的是 Java 代码配置,那么可以在配置类中进行统一配置。
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://domain.com", "http://domain2.com", "http://localhost:63342")
.allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS")
.allowCredentials(false);
}
};
}
『完』