在谈 Spring Boot 中的跨域问题之前,我想我们有必要先了解下同源策略
1 同源策略
不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。该策略是浏览器安全的基石。
也许大家很疑惑,何为’'源“?何为“同源”?其实,源可以理解为协议,域名和端口号的组合,而同源指的是地址中的协议,域名和端口号均相同。
由于浏览器同源策略的影响,不是同源的脚本是不能操作其他源下面的对象的,非同源的网站之间不能发送 AJAX 请求。想要操作另一个源下的对象就需要跨域,其中跨域可以分为 JSONP 跨域和 CORS 跨域。
2 JSONP 跨域和 CORS 跨域
CORS,即 Cross-Origin Resource Sharing,中文意思为跨域资源共享。它定义了在跨域访问资源时浏览器和服务器之间如何进行通信。
CROS 是现在主流解决跨域问题的方案,它与 JSONP 之间的区别如下:
- JSONP 只能实现 GET 请求,而 CORS 支持所有类型的HTTP请求。
- 使用 CORS,开发者可以使用普通的 XMLHttpRequest 发起请求和获得数据,比起 JSONP 有更好的错误处理。
- JSONP 主要被老的浏览器支持,它们往往不支持 CORS ,而绝大多数现代浏览器都已经支持了 CORS。
总的来说,CORS 与 JSONP 相比,更为先进,方便和可靠。
3 CORS 跨域简介
CORS 将请求分为两类:简单请求和非简单请求。
简单请求
下面是一个简单的例子:
GET /test HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, sdch, br
Origin: http://www.examples.com
Host: www.examples.com
对于简单请求,CORS 的策略是请求时在请求头中增加一个 Origin 字段,服务器收到请求后,根据该字段判断是否允许该请求访问。若允许,则在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段,并返回正确的结果,否则不会在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段。
非简单请求
对于非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次 OPTION 请求,称为预检请求。预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到 HTTP 头信息字段中,询问服务器是否允许这样的操作。例子如下:
OPTIONS /test HTTP/1.1
Origin: http://www.examples.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Custom-Header
Host: www.examples.com
服务器收到请求时,需要分别对 Origin、Access-Control-Request-Method(请求使用的 HTTP 方法)、Access-Control-Request-Headers(请求中包含的自定义头字段) 进行验证,验证通过后,会在返回 HTTP头信息中添加 :
Access-Control-Allow-Origin: http://www.examples.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
其含义为:
- Access-Control-Allow-Methods: 真实请求允许的方法
- Access-Control-Allow-Headers: 服务器允许使用的字段
- Access-Control-Allow-Credentials: 是否允许用户发送、处理 cookie
- Access-Control-Max-Age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求
当预检请求通过后,浏览器会发送真实请求到服务器。这就实现了跨源请求。
4 模拟跨域请求
我们先不使用 CORS,看看接下来会发生什么?
我们建立两个 Spring Boot ,分别命名为 test 和 demo。
test 项目的控制器:
package edu.szu.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("index")
public String index(){
return "index" ;
}
}
application.properties:
# 页面默认前缀目录
spring.mvc.view.prefix=/WEB-INF/jsp/
# 响应页面默认后缀
spring.mvc.view.suffix=.jsp
# 指定端口
server.port=8080
test/src/main/webapp/WEB-INF/jsp/index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello 8080!
</body>
</html>
demo 项目的控制器:
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/index")
public String index(){
return "index" ;
}
}
application.properties:
# 页面默认前缀目录
spring.mvc.view.prefix=/WEB-INF/jsp/
# 响应页面默认后缀
spring.mvc.view.suffix=.jsp
# 指定端口
server.port=8087
demo/src/main/webapp/WEB-INF/jsp/index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
Hello 8087!
<input type="button" value="commit" onclick="test()"/>
</body>
<script type="text/javascript">
function test(){
$.ajax({
url:'http://localhost:8080/index',
type:'post',
dataType:'text',
success:function(data){
console.log(data);
}
});
}
</script>
</html>
运行 test 和 demo 两个项目的启动类,在浏览器中输入 http://localhost:8087/index,发现页面一直无法跳转。打开控制台,发现如下报错。
翻译一下,由于在请求的资源上没有 Access-Control-Allow-Origin 的头部 header,8087这个域没有访问权限,XMLHttpRequest 不能加载8080端口的数据。
5 Spring Boot 配置 CORS 的方法
通过 @CrossOrigin 实现
在方法或类上添加 @CrossOrigin 注解,可以直接配置 CORS。
@Controller
public class IndexController {
@RequestMapping("index")
@CrossOrigin("http://localhost:8087")
public String index(){
return "index" ;
}
}
配置过滤器
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
//是否允许请求带有验证信息
corsConfiguration.setAllowCredentials(true);
//允许访问的客户端域名
corsConfiguration.addAllowedOrigin("*");
//允许服务端访问的客户端请求头
corsConfiguration.addAllowedHeader("*");
//允许访问的方法名,GET POST等
corsConfiguration.addAllowedMethod("*");
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
该方式针对全局配置有效。
配置拦截器
@Configuration
public class MyConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedHeaders("*")
.allowedOrigins("*")
.allowedMethods("*");
}
}
该方式针对全局配置有效。
参考:SpringBoot配置Cors解决跨域请求问题
CORS解决ajax跨域问题
Spring Boot 2.0 解决跨域问题 SpringBoot2.X
(十三) : SpringBoot设置支持跨域请求