出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

在开发前后端分离的项目时,常常会碰到跨域请求的问题。即由于浏览器的安全性限制,不允许 AJAX 访问协议不同、域名不同、端口号不同的数据接口,否则会出报 No ‘Access-Control-Allow-Origin’ header is present on the requested resource. 错误。

Spring Boot 支持通过设置 CORS(跨源资源共享)来解决跨域请求问题。具体如下两个地方可以进行配置,我们选择一种即可。
java 后端 实现 CORS 跨域请求的方式
- 返回新的CorsFilter
- 重写 WebMvcConfigurer
- 使用注解 @CrossOrigin
- 手动设置响应头 (HttpServletResponse)
- 自定web filter 实现跨域
注意
- CorFilter / WebMvConfigurer / @CrossOrigin 需要 SpringMVC 4.2以上版本才支持,对应springBoot 1.3版本以上
- 上面前两种方式属于全局 CORS 配置,后两种属于局部 CORS配置。如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过 @CrossOrigin 注解来进行细粒度更高的跨域资源控制。
- 其实无论哪种方案,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域
返回新的 CorsFilter(全局跨域)
在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送 Cookie
config.setAllowCredentials(true);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}
使用注解 (局部跨域)
(1)我们可以直接在相应的请求方法上添加 @CrossOrigin 注解,那么该方法则支持跨域。
@RestController
public class WebAPIController {
@Autowired
DeviceDataManager deviceDataManager;
@GetMapping("/getDeviceDatas")
@CrossOrigin
public List<DeviceData> getDeviceDatas() {
return deviceDataManager.getDatas();
}
}
(2)我们也可以在控制器上添加 @CrossOrigin 注解,那么该控制器下的所有方法都支持跨域。
@RestController
@CrossOrigin
public class WebAPIController {
@Autowired
DeviceDataManager deviceDataManager;
@GetMapping("/getDeviceDatas")
public List<DeviceData> getDeviceDatas() {
return deviceDataManager.getDatas();
}
}
(3)@CrossOrigin 注解还支持更加丰富的参数配置:
- value:表示支持的域。这里表示来自 http://localhost:8081 域的请求是支持跨域的。默认为 *,表示所有域都可以。
- maxAge:表示探测请求的有效期(先进性判断是否有效)。探测请求不用每次都发送,可以配置一个有效期,有效期过了之后才会发送探测请求。默认为 1800 秒,即 30 分钟。
- allowedHeaders:表示允许的请求头。默认为 *,表示该域中的所有的请求都被允许。
@RestController
public class WebAPIController {
@Autowired
DeviceDataManager deviceDataManager;
@GetMapping("/getDeviceDatas")
@CrossOrigin(value = "http://localhost:8081", maxAge = 1800, allowedHeaders ="*")
public List<DeviceData> getDeviceDatas() {
return deviceDataManager.getDatas();
}
}
重写 WebMvcConfigurer(全局跨域)
(1)全局配置需要添加自定义类实现 WebMvcConfigurer 接口,然后实现接口中的 addCorsMappings 方法。下面是一个简单的样例代码:
- addMapping:表示对哪种格式的请求路径进行跨域处理。
- allowedHeaders:表示允许的请求头,默认允许所有的请求头信息。
- allowedMethods:表示允许的请求方法,默认是 GET、POST 和 HEAD。这里配置为 * 表示支持所有的请求方法。
- maxAge:表示探测请求的有效期
- allowedOrigins 表示支持的域
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/device/**")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(1800)
.allowedOrigins("http://localhost:8081");
}
}
(2)我们也可以采用如下配置,直接让所有请求、所有域都支持跨域:
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(1800)
.allowedOrigins("*");
}
}
手动设置响应头(局部跨域)
使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 “*”,表示全部放行。
@RequestMapping("/index")
public String index(HttpServletResponse response) {
response.addHeader("Access-Allow-Control-Origin","*");
return "index";
}
使用自定义filter实现跨域
首先编写一个过滤器,可以起名字为MyCorsFilter.java
package com.mesnac.aop;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
@Component
public class MyCorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
在web.xml中配置这个过滤器,使其生效
<!-- 跨域访问 START--> <filter> <filter-name>CorsFilter</filter-name> <filter-class>com.mesnac.aop.MyCorsFilter</filter-class> </filter> <filter-mapping> <filter-name>CorsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 跨域访问 END -->