1. 跨域问题、同源请求和跨域请求
简单来说,就是前台调用后台接口的时候,由于「浏览器」的同源策略<small>(Same-origin Policy)</small>过于严格,如果这个接口不是同一个域的,就会产生跨域问题。
所谓同源是指:域名、协议、端口相同。
如果发出去的请求不是本域的「协议、域名、端口」,任何一个不一样,浏览器就认为是跨域的。
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<small>(XMLHTTPRequest)</small>请求才会产生跨域问题。如果发出去的请求不是 XHR 请求的话,即使跨域,浏览器也不会报错。
2. CORS 之前的跨域问题的解决办法
-
<h3>方案一:关闭浏览器的跨域限制功能</h3>
这是最简单粗暴,也是最不可能采用的方案。
-
<h3>方案二:JSONP(JSON with Padding)</h3>
JSONP 方案是一个早期方案,也是一个非标准方案。不建议使用。
它的解题思路是,既然只有发送的是 XHR(XMLHTTPRequest)请求才会有可能产生跨域问题<small>(请求被浏览器拦截/阻止)</small>,那么我们就想办法「把 XHR 请求改为非 XHR 请求」。只要不是 XHR 请求,浏览器就不会报跨域预警,就会放行请求。
由于该方案是一个早期的非标准方案,且存在若干缺点,因此慢慢被后续的 CORS 所取代。此处不作讲解。
3. 现行的标准的方案:CORS
CORS<small>(Cross-origin resource sharing,跨域资源共享)</small>是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。
简单来说,就是浏览器「一刀切式地阻止一切跨域请求」过于的粗暴,因此,CORS 的方案就是「让服务器决定是否响应这个跨域请求」。服务器不响应,浏览器就阻止显示响应结果;服务器愿意响应,浏览器就放行,展示响应结果。
整个 CORS 通信过程,都是浏览器自动完成<small>(基本上目前所有的浏览器都实现了 CORS 标准)</small>,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但整个过程用户不会有感觉。
浏览器首先会发起一个请求方法为 OPTIONS 的「预检请求」,用于询问/确认浏服务器是否允许跨域请求,只有在得到服务器的许可后才会发出实际请求。
4. Spring Boot 对 CORS 的支持
环境准备
首先是支持 RESTful 的 Controller,这里就不使用数据库了,简单一点。
-
UserController
@RestController public class UserController { /** * 查询用户列表 */ @GetMapping("/users") public 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 页面,修改端口为其他,然后启动 springboot 。
<!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);
}
};
}
网友评论