簡體   English   中英

未為 OPTIONS/DELETE 正確啟用 Spring Boot Data Rest + CORS

[英]Spring Boot Data Rest + CORS not being enabled properly for OPTIONS/DELETE

我有一個非常簡單的例子,我無法開始工作。

我有我的域來模擬我的數據庫和我的存儲庫。

public interface MyTestRepository extends CrudRepository<MyTest, Integer> {
}

我使用http://resttesttest.com/來測試它。 對於 GET 方法,它會毫無問題地返回 JSON REST 信息。

我可以查詢端點http://localhost:8080/mytest/1並從數據庫中獲取 id=1 的信息。

但是,當我嘗試使用 DELETE 選項時,問題就出現了。 如果我在http://localhost:8080/mytest/1上運行 DELETE 我得到

對預檢請求的響應未通過訪問控制檢查:請求的資源上不存在“Access-Control-Allow-Origin”標頭。 因此,不允許訪問 Origin ' http://resttesttest.com '。 響應具有 HTTP 狀態代碼 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);
}

我用谷歌搜索並找到了這個。

如何在 Spring Boot + Spring Security 應用程序中配置 CORS?

所以我加了

@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;
}

我也找到了這個線程。

Spring Data Rest 和 Cors

並嘗試了以下代碼,但沒有運氣。

@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 明智地通過,但是即使我有“*”,我仍然不斷得到 No '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: https://stackoverflow.com/questions/39565438/no-access-control-allow-origin-error-with-spring-restful-hosted-in-pivotal-web
 */
@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() {}

}

以下配置適用於基於 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);
  }
}

這是我用作允許所有 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 Boot 2.2.6

我不得不添加一個過濾器以允許 OPTIONS 工作。 沒有它,我得到了一個 403 Forbidden。 “Origin”請求標頭是觸發 403 的原因 - 我在 Postman 中進行了測試,並且沒有發送該標頭 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 方法請求 OPTIONS 時,響應會忽略 Access-Control-Allow-Methods 標頭:

@CrossOrigin
public class ArticleController {

@DeleteMapping("/{uuid}")
public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
    articleService.delete(uuid);
}

如果我為 DELETE 卷曲,我會得到一個 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:我已經能夠使用以下控制器代碼解決這個問題:

@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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM