在Spring Boot 3中,解决跨域请求(CORS,Cross-Origin Resource Sharing)的问题主要有以下几种方式:
1. 使用@CrossOrigin
注解
你可以直接在Controller类或者具体的请求处理方法上使用@CrossOrigin
注解来允许跨域请求。
@RestController
@RequestMapping("/user")
@CrossOrigin(origins = "*") // 允许所有来源的请求跨域
public class UserController {
// ...
}
在这个例子中,@CrossOrigin
注解被添加到了控制器类上,表示这个控制器下的所有方法都允许来自http://example.com
的GET和POST请求。你也可以将注解添加到特定的方法上,以对该方法应用CORS配置。
2. 全局配置CORS
如果你希望全局配置CORS,而不是在每个Controller或方法上单独配置,你可以创建一个配置类来实现WebMvcConfigurer
接口,并重写addCorsMappings
方法。
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 添加映射路径
registry.addMapping("/**")
.allowedOrigins("*") // 允许哪些域的请求,星号代表允许所有
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") // 允许的方法
.allowedHeaders("*") // 允许的头部设置
.allowCredentials(true) // 是否发送cookie
.maxAge(168000); // 预检间隔时间
}
}
在这个配置中,addMapping("/**")
表示对所有的路径都应用CORS配置。allowedOrigins("*")
表示允许所有来源的访问,这在生产环境中可能不是最佳实践,通常你会指定具体的域名。allowedMethods
定义了允许的HTTP方法,allowedHeaders
定义了允许的HTTP头部,allowCredentials(true)
表示是否允许携带凭证(cookies, HTTP认证及客户端SSL证明等),maxAge
则用于设置预检请求的有效期。
3. 使用Filter实现CORS
你也可以通过实现Filter
接口来自定义CORS处理逻辑。
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SimpleCorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://example.com");
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");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}
然后需要在配置类中注册这个Filter。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<SimpleCorsFilter> corsFilter() {
FilterRegistrationBean<SimpleCorsFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SimpleCorsFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
4. 使用拦截器(Interceptor)
如果需要更复杂的CORS逻辑,你可以创建一个拦截器来处理CORS请求。拦截器允许你在请求处理之前或之后添加逻辑。
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CorsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
response.setHeader("Access-Control-Allow-Origin", "http://example.com");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "*");
// 其他CORS相关的响应头设置
return true;
}
}
然后,你需要在配置类中注册这个拦截器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CorsInterceptor corsInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
}
}
- 当设置
allowedHeaders("*")
时,实际上浏览器会发送实际请求头而不是*
。出于安全考虑,最好明确指定允许的头部。 - 在生产环境中,确保不要过于宽松地配置CORS,只允许必要的源和方法。
- 如果你的应用部署在代理服务器后面(如Nginx或Apache),可能还需要在代理服务器上配置CORS。
5. 响应体(Response Body)来设置CORS
虽然这种方式不如前面提到的几种方法直接和常用,但在某些特殊场景下,你可能需要手动控制响应头来实现跨域。
具体实现时,你可以在Controller的方法中,通过HttpServletResponse对象来设置Access-Control-Allow-Origin等CORS相关的HTTP头。
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping("/data")
public String getData(HttpServletResponse response) {
// 设置允许跨域的来源
response.setHeader("Access-Control-Allow-Origin", "http://example.com");
// 设置允许的方法(GET, POST, PUT, DELETE等)
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
// 设置允许的头信息
response.setHeader("Access-Control-Allow-Headers", "*");
// 设置预检请求的有效期
response.setHeader("Access-Control-Max-Age", "3600");
// 返回数据
return "Data";
}
}
需要注意的是,手动设置响应头的方式相对繁琐,且容易遗漏某些必要的头信息,导致CORS配置不完整。因此,在实际开发中,推荐使用前面提到的方法,它们更为直接且易于管理。
此外,如果你正在使用Spring Security,还需要确保Spring Security的配置不会阻止跨域请求的处理。在某些情况下,你可能需要在Spring Security的配置中允许特定的CORS请求。
6. 通过自定义ResponseBodyAdvice
ResponseBodyAdvice
是Spring MVC提供的一个接口,允许你在Controller方法返回响应体之前对其进行修改。虽然它本身不是专为CORS设计的,但你可以利用它在返回响应之前添加CORS相关的HTTP头。
下面是一个简单的示例,展示了如何通过实现ResponseBodyAdvice
接口来添加CORS头:
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.lang.reflect.Type;
public class CorsResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 支持所有返回类型
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 设置CORS相关的HTTP头
response.getHeaders().set("Access-Control-Allow-Origin", "http://example.com");
response.getHeaders().set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.getHeaders().set("Access-Control-Allow-Headers", "*");
// 如果有需要,还可以设置其他CORS相关的头,比如预检请求的有效期等
// 返回原始的body,不做修改
return body;
}
}
然后,你需要在Spring Boot的配置中注册这个ResponseBodyAdvice
:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 注册你的ResponseBodyAdvice
converters.forEach(converter -> {
if (converter instanceof MappingJackson2HttpMessageConverter) {
((MappingJackson2HttpMessageConverter) converter).setResponseBodyAdvice(new CorsResponseBodyAdvice());
}
});
}
}
这种方法的优点在于它可以全局地应用于所有Controller的响应,而无需在每个Controller或方法上单独设置。然而,它同样也有一些局限性,比如你可能需要手动处理一些CORS的细节,并且这种方式不如使用Spring提供的CORS支持那么直接和灵活。
在选择解决方案时,应该根据项目的具体需求和团队的偏好来权衡各种方法的优缺点。如果项目中有大量的Controller需要处理跨域请求,并且希望有一个统一且全局的解决方案,那么使用WebMvcConfigurer
或CorsFilter
可能是更好的选择。如果只需要在特定的Controller或方法上处理跨域请求,那么使用@CrossOrigin
注解可能更为简单直接。