跨域问题每个人都会遇到,特别是在前后端分离的系统中。
如果你正在使用 Spring Boot 开发后端应用,并且浏览器中遇到了跨域问题。类似于如下:
Access to fetch at 'http://127.0.0.1/demo' from origin 'https://springdoc.cn' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
但是你又不想去了解跨域、CORS 这些到底是个啥,你只想快速地解决这个问题。
那你可以把如下配置类,添加到你的 Spring Boot 应用中,它可以解决 99% 以上的跨域问题:
// TODO package 声明
import java.time.Duration;
import java.util.Arrays;
import java.util.stream.Stream;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.filter.CorsFilter;
/**
*
* 全局跨域配置
*
*/
@Configuration
public class CorsFilterConfiguration {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
CorsFilter corsFilter = new CorsFilter(request -> {
String origin = request.getHeader(HttpHeaders.ORIGIN);
if (!StringUtils.hasText(origin)) {
// 非跨域请求
return null;
}
CorsConfiguration config = new CorsConfiguration();
// 允许所有域
config.addAllowedOrigin(origin);
// 允许所有请求 Header
String requestHeders = request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
if (StringUtils.hasText(requestHeders)) {
config.setAllowedHeaders(Stream.of(requestHeders.split(",")).map(String::trim).distinct().toList());
}
// 默认允许 Javascript 访问的响应头
config.setExposedHeaders(Arrays.asList("Cache-Control", "Content-Language", "Content-Length",
"Content-Type", "Expires", "Last-Modified", "Pragma"));
// 允许携带凭证
config.setAllowCredentials(true);
// 允许所有请求方法
config.setAllowedMethods(Arrays.asList("GET", "HEAD", "POST", "PUT",
"PATCH", "DELETE", "OPTIONS", "TRACE"));
// 预检缓存 30 分钟
config.setMaxAge(Duration.ofMinutes(30));
return config;
});
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>(corsFilter);
registrationBean.addUrlPatterns("/*"); // 拦截所有请求
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最先执行
return registrationBean;
}
}
简单说几个要点。
如果你使用这个配置类,那么就没必要使用 Spring / Spring Security 提供的其他跨域解决方案了,例如:WebMvcConfigurer
接口的 addCorsMappings
配置方法、@CrossOrigin
注解。
该配置类,第一行没有 package
声明,你要自己添加。它应该在你的 @SpringBootApplication
子包下,以便被 Spring Boot 自动扫描加载。
如,放到 cn.springdoc.demo.config
包下:
src/main/java
|-cn.springdoc.demo.config
|-CorsFilterConfiguration.java
|-cn.springdoc.demo
|-DemoAppplication.java // @SpringBootApplication 类
注意 :代码中的 config.setExposedHeaders(...)
方法,设置了允许被 Javascript 访问的响应头。如果你响应了额外的 Header,且需要被客户端访问。那么需要手动添加。
例如:你的登录接口在登录成功后会响应包含了 Token 的 Header:X-Auth-Token
,客户端需要访问这个 Header 来读取、保存 Token。你可以在新的一行中使用 addExposedHeader
方法添加:
config.addExposedHeader("X-Auth-Token");
最后,如果你有兴趣了解一下关于 CORS 跨域的详细信息,推荐阅读《Spring 和 CORS 跨域》。