Spring CORS 仅在某些资源上失败

Posted

技术标签:

【中文标题】Spring CORS 仅在某些资源上失败【英文标题】:Spring CORS fails ONLY on some resources 【发布时间】:2019-05-17 03:59:43 【问题描述】:

我有一个 RESTful Web 服务,其中包含许多不同的资源(端点),当从 Postman 调用时,所有这些资源都可以正常工作。但是,只有其中一些可以在浏览器中运行,而其余的则失败。我无法在失败的 URL 和没有失败的 URL 中发现任何模式。

我直接从 Chrome 控制台调用资源。

例如,这是在浏览器中失败的资源,但在 Postman 中有效 - /api/hello/

fetch("https://myapp.herokuapp.com/api/hello/", method: "GET", headers: "authToken" : "7c577770c0b7f1b2ba0f7dbb10ae247978a9997cf8aec3bbab0ac7de409e0b4c")
.then(res => res.json())  
.then(console.log)

例如,这是 Postman 和 Chrome 都可以使用的资源。

fetch("https://myapp.herokuapp.com/api/user/all/", method: "GET", headers: "authToken" : "7c577770c0b7f1b2ba0f7dbb10ae247978a9997cf8aec3bbab0ac7de409e0b4c")
.then(res => res.json())  
.then(console.log)

对于失败的资源,Chrome 记录了以下错误。

Access to fetch at 'https://myapp.herokuapp.com/api/hello/' from origin 'http://admin.myapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

我观察到,如果我尝试获取我的服务中实际上不存在的资源,则会出现完全相同的消息,例如调用 /api/blabla/ 会导致完全相同的错误.

我在 Spring 中的 CORS 配置如下。

@Component
public class SimpleCORSFilter implements Filter 

    private static final String DEFAULT_ORIGIN = "https://admin.myapp.com";

    private static final Set<String> allowedOrigins = new HashSet<>();
    static 
        allowedOrigins.add(DEFAULT_ORIGIN);
        allowedOrigins.add("http://admin.myapp.com");
    

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;

        final String origin = request.getHeader("Origin");

        if (origin != null && allowedOrigins.contains(origin)) 
            // set origin to our specific domain to disallow request from other sites
            response.setHeader("Access-Control-Allow-Origin", origin);
         else 
            // set origin to our default allowed
            response.setHeader("Access-Control-Allow-Origin", DEFAULT_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", String.format("%s, %s", "Content-Type", HEADER_AUTH_TOKEN));

        chain.doFilter(req, res);
    

    @Override
    public void init(FilterConfig filterConfig) 
    

    @Override
    public void destroy() 
    

我可以看到来自 Postman 的任何调用以及来自 Chrome 的未报告错误的调用的响应标头都已完美设置。但是,在来自 Chrome 的确实报告错误的调用中,Access-Control-Allowed-Methods 仅包含 GET。

TL;DR - 我不知道为什么来自浏览器的 CORS 调用仅对某些资源失败,而对于其他资源却可以正常工作。

【问题讨论】:

引用的错误表明服务器正在记录消息 “对预检请求的响应未通过访问控制检查:它没有 HTTP ok 状态。” 所以当这种情况发生时,在浏览器 devtools 的 Network 窗格中检查请求并检查响应的 HTTP 状态代码。到目前为止,您在问题中获得的信息表明,对于向https://myapp.herokuapp.com/api/hello/ 发出的 CORS 预检 OPTIONS 请求,服务器没有根据需要以 200 OK 响应,因此预检失败。您需要使服务器以 200 OK 响应对该路由的 OPTIONS 请求。 【参考方案1】:

Spring 自动处理 CORS preflight 请求。

但您并没有真正使用Spring's CORS functionality,因此您应该通过返回 HTTP 200 OK 来处理 OPTIONS 请求。

类似这样的:

    if ("OPTIONS".equals(req.getMethod())) 
        res.setStatus(HttpServletResponse.SC_OK);
     else 
        chain.doFilter(req, res);
    

(虽然我不认为这是你问题的根本原因)

【讨论】:

以上是关于Spring CORS 仅在某些资源上失败的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS 中的跨域 HTTP 请求失败

将 Spring Security(双向 SSL)配置为仅对 CORS 预检 OPTIONS 请求进行身份验证

仅在 Angular 资源请求中缺少 CORS 标头

Firefox 在 CORS 资源上失败,而 Chrome 和 Safari 工作

Spring Data Rest 和 Cors

在 CORS 期间 Spring-Boot RestController 失败