Spring Boot Data Rest + CORS 未正确启用 OPTIONS/DELETE
Spring Boot Data Rest + CORS not being enabled properly for OPTIONS/DELETE
我有一个非常简单的例子,我无法开始工作。
我有自己的域来模拟我的数据库和存储库。
public interface MyTestRepository extends CrudRepository<MyTest, Integer> {
}
我用http://resttesttest.com/测试了一下。对于 GET 方法,它 returns 我 JSON REST 信息没有任何问题。
我可以查询端点 http://localhost:8080/mytest/1 并从数据库中取回 id=1 的信息。
但是,当我尝试使用 DELETE 选项时,问题就来了。如果我 运行 在 http://localhost:8080/mytest/1 上删除,我得到
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://resttesttest.com' is therefore not allowed
access. The response had HTTP status code 403.
我最初尝试了以下方法,但发现我无法使用它,因为我正在使用 Spring-data-Rest。 https://jira.spring.io/browse/DATAREST-573
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true).maxAge(3600);
}
我用谷歌搜索并找到了这个。
How to configure CORS in a Spring Boot + Spring Security application?
所以我添加了
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
我也找到了这个帖子。
也尝试了以下代码,但没有成功。
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
// return new CorsFilter(source);
final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
我添加了一个 catch all 测试,它应该允许所有 CORS 明智地通过,但是我仍然得到否 'Access-Control-Allow-Origin',即使我有“*”。
在这一点上,我不知道为什么预检请求没有通过访问控制检查我错过了什么。
curl 发出删除没有问题。
编辑:
最终找到了确切的解决方案。我不确定我所拥有的与此方法之间的差异,但这似乎有效。
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Note this is a very simple CORS filter that is wide open.
* This would need to be locked down.
* Source:
*/
@Component
public class CORSFilter 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, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
这是我用作允许所有 CORS servlet 过滤器的过滤器:
public class PermissiveCORSFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(PermissiveCORSFilter.class);
private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9 ,-_]*$");
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
String origin;
String credentialFlag;
if (request.getHeader("Origin") == null) {
origin = "*";
credentialFlag = "false";
} else {
origin = request.getHeader("Origin");
credentialFlag = "true";
}
// need to do origin.toString() to avoid findbugs error about response splitting
response.addHeader("Access-Control-Allow-Origin", origin.toString());
response.setHeader("Access-Control-Allow-Credentials", credentialFlag);
if ("OPTIONS".equals(request.getMethod())) {
LOGGER.info("Received OPTIONS request from origin:" + request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET,POST,HEAD,OPTIONS,PUT,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
String headers = StringUtils.trimToEmpty(request.getHeader("Access-Control-Request-Headers"));
if (!PATTERN.matcher(headers).matches()) {
throw new ServletException("Invalid value provided for 'Access-Control-Request-Headers' header");
}
response.setHeader("Access-Control-Allow-Headers", headers); // allow any headers
}
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {
// Do nothing
}
@Override
public void destroy() {
// Do nothing
}
以下配置适用于基于 Spring Data Rest 的应用程序。需要注意的重要一点是,过滤器已注册为在安全过滤器链启动之前执行。
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
@Override
public void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(corsFilter(), ChannelProcessingFilter.class);
}
@Bean
protected Filter corsFilter()
{
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
config.addExposedHeader("Location");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
使用 Spring 启动 2.2.6
我必须添加一个过滤器才能让 OPTIONS 起作用。没有它,我得到了 403 Forbidden。 “Origin”请求 header 是触发 403 的原因 - 我在 Postman 中进行了测试,但没有发送 header OPTIONS 在没有过滤器的情况下工作。
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
@Component
public class CORSFilter 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", "OPTIONS"); // "POST, GET, PUT, OPTIONS, DELETE"
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
与
一起
@Configuration
public class ConfigCORS implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") allowedOrigins("http://localhost:3000")
.allowedMethods("POST", "PUT", "GET", "DELETE", "OPTIONS")
.allowedHeaders("Content-Type", "Origin")
.exposedHeaders("X-Total-Count", "Location", "Access-Control-Allow-Origin")
.allowCredentials(false)
.maxAge(6000);
}
}
我好像也遇到了同样的问题。 CrossOrigin 配置与 GET/PUT/POST 一起工作正常,但是当我为我的 Spring PostMapping 方法请求选项时,响应省略了 Access-Control-Allow-Methods header:
@CrossOrigin
public class ArticleController {
@DeleteMapping("/{uuid}")
public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
articleService.delete(uuid);
}
如果我 curl 删除,我得到一个 HTTP 200,包括 Access-Control-Allow-Methods:
$ curl -v -H "Access-Control-Request-Method: DELETE" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
< HTTP/1.1 200
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: PUT,POST,GET,DELETE,OPTIONS
< Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
如果我卷曲选择 OPTIONS,我会收到 403:
$ curl -v -H "Access-Control-Request-Method: OPTIONS" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
< HTTP/1.1 403
我是不是漏掉了什么?
编辑 1:
如果我将此映射添加到控制器(基于 Enable CORS for OPTIONS request using Spring Framework ):
@RequestMapping(
value = "/**",
method = RequestMethod.OPTIONS
)
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}
这导致:
$ curl -v -H "Access-Control-Request-Method: OPTIONS" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
< HTTP/1.1 200
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: OPTIONS
< Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
但它并没有解决 Angular 的问题,它仍然给出 403
编辑 2:
我已经能够通过以下 Controller-code:
解决这个问题
@RequestMapping("/article")
@CrossOrigin(origins="http://localhost:4200",
methods = {RequestMethod.PUT, RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE, RequestMethod.OPTIONS}
)
public class ArticleController {
@RequestMapping(
value = "/{uuid}",
method = { RequestMethod.DELETE })
public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
articleService.delete(uuid);
}
@RequestMapping(method = { RequestMethod.OPTIONS})
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}
我有一个非常简单的例子,我无法开始工作。
我有自己的域来模拟我的数据库和存储库。
public interface MyTestRepository extends CrudRepository<MyTest, Integer> {
}
我用http://resttesttest.com/测试了一下。对于 GET 方法,它 returns 我 JSON REST 信息没有任何问题。
我可以查询端点 http://localhost:8080/mytest/1 并从数据库中取回 id=1 的信息。
但是,当我尝试使用 DELETE 选项时,问题就来了。如果我 运行 在 http://localhost:8080/mytest/1 上删除,我得到
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://resttesttest.com' is therefore not allowed access. The response had HTTP status code 403.
我最初尝试了以下方法,但发现我无法使用它,因为我正在使用 Spring-data-Rest。 https://jira.spring.io/browse/DATAREST-573
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true).maxAge(3600);
}
我用谷歌搜索并找到了这个。
How to configure CORS in a Spring Boot + Spring Security application?
所以我添加了
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
我也找到了这个帖子。
也尝试了以下代码,但没有成功。
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
// return new CorsFilter(source);
final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
我添加了一个 catch all 测试,它应该允许所有 CORS 明智地通过,但是我仍然得到否 'Access-Control-Allow-Origin',即使我有“*”。
在这一点上,我不知道为什么预检请求没有通过访问控制检查我错过了什么。
curl 发出删除没有问题。
编辑:
最终找到了确切的解决方案。我不确定我所拥有的与此方法之间的差异,但这似乎有效。
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Note this is a very simple CORS filter that is wide open.
* This would need to be locked down.
* Source:
*/
@Component
public class CORSFilter 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, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
这是我用作允许所有 CORS servlet 过滤器的过滤器:
public class PermissiveCORSFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(PermissiveCORSFilter.class);
private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9 ,-_]*$");
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
String origin;
String credentialFlag;
if (request.getHeader("Origin") == null) {
origin = "*";
credentialFlag = "false";
} else {
origin = request.getHeader("Origin");
credentialFlag = "true";
}
// need to do origin.toString() to avoid findbugs error about response splitting
response.addHeader("Access-Control-Allow-Origin", origin.toString());
response.setHeader("Access-Control-Allow-Credentials", credentialFlag);
if ("OPTIONS".equals(request.getMethod())) {
LOGGER.info("Received OPTIONS request from origin:" + request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET,POST,HEAD,OPTIONS,PUT,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
String headers = StringUtils.trimToEmpty(request.getHeader("Access-Control-Request-Headers"));
if (!PATTERN.matcher(headers).matches()) {
throw new ServletException("Invalid value provided for 'Access-Control-Request-Headers' header");
}
response.setHeader("Access-Control-Allow-Headers", headers); // allow any headers
}
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {
// Do nothing
}
@Override
public void destroy() {
// Do nothing
}
以下配置适用于基于 Spring Data Rest 的应用程序。需要注意的重要一点是,过滤器已注册为在安全过滤器链启动之前执行。
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
@Override
public void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(corsFilter(), ChannelProcessingFilter.class);
}
@Bean
protected Filter corsFilter()
{
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
config.addExposedHeader("Location");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
使用 Spring 启动 2.2.6
我必须添加一个过滤器才能让 OPTIONS 起作用。没有它,我得到了 403 Forbidden。 “Origin”请求 header 是触发 403 的原因 - 我在 Postman 中进行了测试,但没有发送 header OPTIONS 在没有过滤器的情况下工作。
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
@Component
public class CORSFilter 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", "OPTIONS"); // "POST, GET, PUT, OPTIONS, DELETE"
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
与
一起@Configuration
public class ConfigCORS implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") allowedOrigins("http://localhost:3000")
.allowedMethods("POST", "PUT", "GET", "DELETE", "OPTIONS")
.allowedHeaders("Content-Type", "Origin")
.exposedHeaders("X-Total-Count", "Location", "Access-Control-Allow-Origin")
.allowCredentials(false)
.maxAge(6000);
}
}
我好像也遇到了同样的问题。 CrossOrigin 配置与 GET/PUT/POST 一起工作正常,但是当我为我的 Spring PostMapping 方法请求选项时,响应省略了 Access-Control-Allow-Methods header:
@CrossOrigin
public class ArticleController {
@DeleteMapping("/{uuid}")
public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
articleService.delete(uuid);
}
如果我 curl 删除,我得到一个 HTTP 200,包括 Access-Control-Allow-Methods:
$ curl -v -H "Access-Control-Request-Method: DELETE" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
< HTTP/1.1 200
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: PUT,POST,GET,DELETE,OPTIONS
< Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
如果我卷曲选择 OPTIONS,我会收到 403:
$ curl -v -H "Access-Control-Request-Method: OPTIONS" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
< HTTP/1.1 403
我是不是漏掉了什么?
编辑 1:
如果我将此映射添加到控制器(基于 Enable CORS for OPTIONS request using Spring Framework ):
@RequestMapping(
value = "/**",
method = RequestMethod.OPTIONS
)
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}
这导致:
$ curl -v -H "Access-Control-Request-Method: OPTIONS" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
< HTTP/1.1 200
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: OPTIONS
< Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
但它并没有解决 Angular 的问题,它仍然给出 403
编辑 2: 我已经能够通过以下 Controller-code:
解决这个问题@RequestMapping("/article")
@CrossOrigin(origins="http://localhost:4200",
methods = {RequestMethod.PUT, RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE, RequestMethod.OPTIONS}
)
public class ArticleController {
@RequestMapping(
value = "/{uuid}",
method = { RequestMethod.DELETE })
public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
articleService.delete(uuid);
}
@RequestMapping(method = { RequestMethod.OPTIONS})
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}