本文总结SpringBoot或者SSM体系下跨域后端处理方案。
【1】SpringBoot
直接在WebConfigurer配置即可,具体属性/值可以根据需要自定义
//跨域配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").
allowedOrigins("*"). //允许跨域的域名,可以用*表示允许任何域名使用
allowedMethods("*"). //允许任何方法(post、get等)
// allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"). //允许任何方法(post、get等)
allowedHeaders("*"). //允许任何请求头
allowCredentials(true). //带上cookie信息
exposedHeaders(HttpHeaders.SET_COOKIE)
.maxAge(3600L);
//maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
}
或者如下所示:
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 设置允许跨域请求的域名, "*"表示允许任意域名使用该Web服务。
config.addAllowedOrigin("*");
// 是否发送Cookie信息
config.setAllowCredentials(true);
// 设置所允许的HTTP请求方法, "*"表示允许所有HTTP请求方法
config.addAllowedMethod("*");
// 设置所允许的请求头, "*"表示允许所有的请求头
config.addAllowedHeader("*");
config.addExposedHeader(HttpHeaders.SET_COOKIE);
config.setMaxAge(3600L);
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
需要注意的是,如果你工程中有拦截器,可能会有影响哦。
【2】SSM体系
这里比较麻烦,需要编写跨域过滤器并配置在web.xml中。
跨域过滤器:
// 跨域过滤器
public class CrosFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String curOrigin = request.getHeader("Origin");
StringBuffer requestURL = request.getRequestURL();
System.out.println("跨域过滤器被执行,当前访问来源者为:" + curOrigin);
System.out.println("跨域过滤器被执行,当前访问来源者为:" + requestURL.toString());
// 设置响应头
//该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接收任意域名的请求。
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");
//该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法
response.setHeader("Access-Control-Allow-Methods", "*");
//预检间隔时间
response.setHeader("Access-Control-Max-Age", "3600");
//该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段
//一旦自己设置了自定义请求头那么在服务端就不能统一放行设置为*,这样是不会通过校验的,
// *一般校验的只有固定的请求头参数,如果是自定义的请求头自然不在这个列表中,所以如果是自定义请求头,后端需要将自定义头逐一校验,这样方能完成预校验
// response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,token,customercoderoute,authorization,conntectionid,Cookie,request-ajax");
//Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。
// 默认情况下,Cookie不包括在CORS请求之中,设为True,
// 即表示服务器明确许可,Cookie可以包含在请求中,一起发送给服务器。
// 这个值也只能设为True,如果服务器不要浏览器发送Cookie,删除即可
// Access-Control-Allow-Credentials为True的时候,Access-Control-Allow-Origin一定不能设置为“*”,否则报错
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Expose-Headers", "*");
// 浏览器默认会发起异常 OPTIONS 的请求方式 这个时候我们通过过滤器直接拦截返回200后就可以解决跨越问题
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
System.out.println("跨域过滤器被执行,chain.doFilter(request, response);" );
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
xml配置:
<filter>
<filter-name>crosFilter</filter-name>
<filter-class>com.jane.config.CrosFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>crosFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
可以看到两种体系下属性的设置基本一致的。
【3】springboot下设置跨域不生效的场景
如果你的项目中有其他拦截器影响了 CORS 的设置,导致全局 CORS 配置不起作用,可以通过调整拦截器的顺序或排除特定路径来解决这个问题。以下是一些可能的解决方案:
解决方案 1:调整拦截器顺序
确保 CORS 配置在所有其他拦截器之前应用。可以通过 Ordered
接口或 order
方法来指定顺序。
示例:使用 @Order(Ordered.HIGHEST_PRECEDENCE)
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) //解决拦截器影响跨域配置问题
public WebMvcConfigurer corsConfigurer(){
// response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").
allowedOrigins("*"). //允许跨域的域名,可以用*表示允许任何域名使用
// allowedMethods("*"). //允许任何方法(post、get等)
allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"). // 允许的HTTP方法
allowedHeaders("*"). //允许任何请求头
allowCredentials(true). //带上cookie信息
exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L);
//maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
}
};
}
解决方案 2:排除特定路径
如果你的拦截器只针对某些路径,可以将这些路径从 CORS 配置中排除。
示例:排除特定路径
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.excludePathPatterns("/no-cors/**") // 排除这些路径
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
解决方案 3:在拦截器中处理 CORS
如果你的拦截器必须处理所有请求,可以在拦截器中处理 CORS 相关的响应头。
示例:在拦截器中/过滤器处理 CORS
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.web.filter.OncePerRequestFilter;
public class CorsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
}
}
注册拦截器
然后在 Spring Boot 的配置类中注册这个拦截器。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(new CorsFilter());
registration.addUrlPatterns("/*"); // 应用于所有 URL
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高的优先级
return registration;
}
}
这里的拦截器是一个广义概念,泛指可以根据路径匹配规则实现请求拦截/过滤的服务。如下是在一个拦截器的preHandle添加响应头实现跨域支持:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//拦截器跨域配置
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization,Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
String token = request.getHeader(AUTH_HEADER_NAME);
if (StringUtils.isEmpty(token)||isTokenExpired(token)) {
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("code", 401);
errorResponse.put("msg", "Token has expired");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);// response.setStatus(401); // Return 401 Unauthorized
response.getWriter().write(JSONObject.toJSONString(errorResponse));
// new ObjectMapper().writeValue(response.getOutputStream(), errorResponse);
return false; // Stop processing the request
}
return true; // Continue processing the request
}
解决方案 4:使用 @CrossOrigin
注解
对于特定的控制器或方法,可以在类级别或方法级别使用 @CrossOrigin
注解。
示例:使用 @CrossOrigin
注解
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "*", allowedHeaders = "*", allowCredentials = "true")
public class MyController {
@GetMapping("/protected")
public String getProtectedResource() {
return "Hello, Protected Resource!";
}
}
总结
- 调整顺序:确保 CORS 配置在所有其他拦截器之前应用。
- 排除路径:如果某些路径不需要 CORS 支持,可以将这些路径排除。
- 在拦截器中处理 CORS:如果拦截器必须处理所有请求,可以在拦截器中添加 CORS 相关的响应头。
- 使用
@CrossOrigin
注解:对于特定的控制器或方法,可以在类级别或方法级别使用@CrossOrigin
注解。
通过这些方法,你可以解决由于其他拦截器导致的 CORS 配置无效的问题。如果仍然存在问题,请提供更多关于你的项目结构和配置的信息,以便进一步诊断。
【4】允许所有来源、请求头和凭证案例
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 使用 allowedOriginPatterns 代替 addAllowedOrigin 以支持通配符和凭证同时使用
config.addAllowedOriginPattern("*"); // 允许所有来源
// 凭证配置需要与来源模式配合使用
config.setAllowCredentials(true); // 允许发送Cookie
// 允许所有请求头
config.addAllowedHeader("*"); // 允许所有请求头
// 允许所有HTTP方法
config.addAllowedMethod("*"); // GET, POST, PUT 等
// 暴露需要访问的响应头(可选)
config.addExposedHeader(HttpHeaders.SET_COOKIE);
// 预检请求缓存时间(秒)
config.setMaxAge(3600L);
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
关键优化点说明:
-
使用
addAllowedOriginPattern("*")
代替多个addAllowedOrigin
调用,可以:- 支持所有来源的跨域请求
- 兼容
setAllowCredentials(true)
配置(使用addAllowedOrigin("*")
会导致与凭证配置冲突)
-
保留
addAllowedHeader("*")
实现允许所有请求头 -
其他优化保留项:
- 保持凭证支持(Cookie、Authorization等)
- 暴露必要的响应头
- 维持预检请求缓存时间
注意:allowedOriginPatterns
是 Spring Framework 5.3+ 引入的特性,请确保你的 Spring Boot 版本 >= 2.4.x。如果使用旧版本,可以改为动态设置来源:
config.setAllowedOrigins(Arrays.asList("*"));
// 但这样会与 setAllowCredentials(true) 冲突,需要二选一
springboot版本如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>