【问题标题】:How to enable Cross domain requests on JAX-RS web services?如何在 JAX-RS Web 服务上启用跨域请求?
【发布时间】:2014-06-20 10:58:47
【问题描述】:

我开发了一套宁静的网络服务。由于错误,我无法从远程客户端调用任何这些方法 No 'Access-Control-Allow-Origin' header is present on the requested resource.

服务在本地主机上完美运行。 服务器端是否有任何更改或配置可以解决问题。即启用跨域请求。

我正在使用 WildFly 8、JavaEE 7

【问题讨论】:

  • 您可以在此处了解有关 CORS 的更多信息:html5rocks.com/en/tutorials/cors 您可以在此处了解如何将 CORS 支持添加到 JAX-RS:cxf.apache.org/docs/jax-rs-cors.html
  • 这很有用,但我一直在寻找不修改客户端代码的解决方案。
  • 您的问题要求对服务器端进行更改。 CORS 的大部分更改都是在服务器端完成的。客户端只需要很少的更改(如果有的话)。

标签: cors jax-rs cross-domain java-ee-7 wildfly-8


【解决方案1】:

我也想知道同样的事情,所以经过一番研究后,我发现最简单的方法就是使用 JAX-RS ContainerResponseFilter 添加相关的 CORS 标头。这样您就不需要用 CXF 替换整个 Web 服务堆栈(Wildfly 使用 CXF 是某种形式,但它看起来不像它用于 JAX-RS 可能只有 JAX-WS)。

无论您是否使用此过滤器,它都会将标头添加到每个 REST Web 服务。

package com.yourdomain.package;

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class CORSFilter implements ContainerResponseFilter {

   @Override
   public void filter(final ContainerRequestContext requestContext,
                      final ContainerResponseContext cres) throws IOException {
      cres.getHeaders().add("Access-Control-Allow-Origin", "*");
      cres.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
      cres.getHeaders().add("Access-Control-Allow-Credentials", "true");
      cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
      cres.getHeaders().add("Access-Control-Max-Age", "1209600");
   }

}

然后,当我使用 curl 进行测试时,响应包含 CORS 标头:

$ curl -D - "http://localhost:8080/rest/test"
HTTP/1.1 200 OK
X-Powered-By: Undertow 1
Access-Control-Allow-Headers: origin, content-type, accept, authorization
Server: Wildfly 8
Date: Tue, 13 May 2014 12:30:00 GMT
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Transfer-Encoding: chunked
Content-Type: application/json
Access-Control-Max-Age: 1209600
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD

我的理解是 @Provider 注释告诉 JAX-RS 运行时使用过滤器,没有注释什么都不会发生。

我从Jersey example 得到了使用ContainerResponseFilter 的想法。

【讨论】:

  • 是的,我使用了您在此处发布的相同解决方案。但我相信我们通过允许每个跨域连接来制造安全问题。
  • 是的,如果您的 REST Web 服务使用 cookie 来保护它们,情况就是如此。但是,如果您只是想创建一些公共 REST Web 服务,那么就不存在安全问题。您没有在问题中提到您是否只需要 1 个域来工作,例如 services.yourdomain 或每个域。
  • @Joel Pearson 同意。我通过谷歌搜索“在 jax-rs 中启用 CORS”遇到了这个问题。只是认为它可能对同样登陆此页面的其他人有所帮助。尽管如此,接受的答案似乎并没有暗示任何特定于 wildfly 或 jee7 的内容。可能是我错过了链接?这是在 jetty+jersey 上对我有用的解决方案,感谢您发布它。
  • Access-Control-Allow-Credentials: true 这一行实际上与"Access-Control-Allow-Origin", "*" 冲突,因为这可能是一个安全问题,或者(至少在 Chrome 中)只是被拒绝。如果要允许凭据,则需要指定特定来源,而不是通配符。
  • 首先获取请求来源:String origin = requestContext.getHeaderString("origin");,然后(当 origin !== null 时)使用该来源作为通配符:cres.getHeaders().add("Access-Control-Allow-Origin", origin);。如果您的 API 使用 cookie 进行身份验证(例如,使用容器管理的身份验证时),请在设置 CORS 标头之前根据允许的来源白名单检查来源。
【解决方案2】:

我遇到了类似的问题,曾尝试使用@Alex Petty 的solution,但除了必须在我的班级中的每个 JAX-RS 端点上设置 CORS 标头之外,如下所示:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMemberList() {
    List<Member> memberList = memberDao.listMembers();
    members.addAll(memberList);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(memberList)
            .build();
}

我必须进一步定义一个包罗万象的 OPTIONS 端点,它将为类中的任何其他 OPTIONS 请求返回 CORS 标头,从而捕获该类别的所有端点:

@OPTIONS
@Path("{path : .*}")
public Response options() {
    return Response.ok("")
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .build();
}

只有在这样做之后,我才能从其他域或主机上的 Jquery Ajax 客户端正确使用我的 JAX-RS API 端点。

【讨论】:

  • 我遇到了一个奇怪的问题。我设置的响应标头 (.header("SOME_HEADER","SOME_HEADER") 没有在 Google chrome、Mozilla 的 swagger UI 中呈现,但在 IE 11 中。我已经检查了这个响应标头是否进入浏览器(在控制台响应中开发人员工具的标头),但同样没有在 Swagger UI 中呈现。
  • 这不正确。你必须使用过滤器。当您想根据特定条件过滤和/或修改请求时,请使用过滤器。当您想要控制、预处理和/或后处理请求时,请使用 Servlet。 docs.oracle.com/javaee/6/tutorial/doc/bnagb.html
  • 我刚刚添加了这个 .header("Access-Control-Allow-Origin", "*") 并且它起作用了
【解决方案3】:

我发现了一种更简单(RestEasy 特定)的方法,无需使用过滤器即可在 Wildfly 上启用 CORS,并且您可以在资源级别控制 API 响应标头配置。

例如:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMemberList() {
    List<Member> memberList = memberDao.listMembers();
    members.addAll(memberList);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(memberList)
            .build();
}

【讨论】:

  • 谢谢。我无法获得“javax.ws.rs.container”类(为什么?另一个 JAX-RS 版本?),您的解决方案运行良好。
  • 这种方式仅适用于 GET,当尝试使用 POST(来自客户端)时会发生相同的错误,我仍在尝试找出如何解决此问题,因为我的“来源”会有所不同时间。
  • 这永远行不通,它错了。 CORS 在调用“GET”、“POST”等之前首先调用 HTTP 方法“OPTIONS”。标题条目必须在“OPTIONS”方法中定义。如果不是,浏览器将不会接受它。 HTTP 请求过滤是最好的方法。另一件事是,如果您使用的是像 Apache 这样的严肃 HTTP 服务器,则条目 "Access-Control-Allow-Origin", "*" 是稀疏的,浏览器不会允许它。您必须专门添加授权 DNS,例如"Access-Control-Allow-Origin: my.domain.com".
  • 但如果你有 100 种方法......更好的方法是使用过滤器
  • @Paul Samsotha 给出了完整的答案以及对飞行前请求的正确解释,这是链接 - stackoverflow.com/a/28067653/3257644
【解决方案4】:

我很幸运地使用这个库为我的 API(在 Wildfly 上)配置了跨域资源共享 (CORS):

<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.1</version>
</dependency>

设置非常简单。只需将上述依赖项添加到您的 pom 中,然后将以下配置添加到您的 web.xml 文件的 webapp 部分。

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

    <init-param>
        <param-name>cors.allowGenericHttpRequests</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowOrigin</param-name>
        <param-value>*</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowSubdomains</param-name>
        <param-value>false</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportedMethods</param-name>
        <param-value>GET, HEAD, POST, DELETE, OPTIONS</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportedHeaders</param-name>
        <param-value>*</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportsCredentials</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>cors.maxAge</param-name>
        <param-value>3600</param-value>
    </init-param>

</filter>

<filter-mapping>
    <!-- CORS Filter mapping -->
    <filter-name>CORS</filter-name>
    <url-pattern>*</url-pattern>
</filter-mapping>

如果您愿意,也可以使用属性文件对其进行配置。这个库就像一个魅力,给你很大的配置灵活性!

【讨论】:

  • 我想试试这个,但我的日志控制台上出现了这个错误:The 'Access-Control-Allow-Origin' header contains multiple values 'http://127.0.0.1, *', but only one is allowed. 一个想法在 web.xml 上进行什么更改以使其正确?
  • 我试过这个,但看不到这个添加到标题中。
【解决方案5】:

没有其他答案对我有用,但这确实有效:

import javax.ws.rs.core.Response;

然后将服务方法的返回类型改为Response,将return语句改为:

return Response.ok(resp).header("Access-Control-Allow-Origin", "*").build();

其中resp 是原始响应对象。

【讨论】:

  • 这仅适用于简单请求。也就是说,如果请求是 GET 或 POST 并且请求标头很简单(唯一的简单标头是 Accept、Accept-Language、Content-Language、Content-Type= application/x-www-form-urlencoded、multipart/form-data、文本/纯文本)。否则浏览器会在实际请求之前发送一个 OPTIONS 请求。
【解决方案6】:

您也可以如下实现javax.ws.rs.core.Feature 来实现CORS。

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
import org.jboss.resteasy.plugins.interceptors.CorsFilter;

@Provider
 public class CorsFeature implements Feature {

  @Override
  public boolean configure(FeatureContext context) {
    CorsFilter corsFilter = new CorsFilter();
    corsFilter.getAllowedOrigins().add("*");
    context.register(corsFilter);
    return true;
 }

}

【讨论】:

    【解决方案7】:

    解决方案会在响应中添加一些标头。在 spring 或 spring boot 中有注释,但在旧系统中,可能没有注释。您可以通过添加过滤器来解决。

    过滤器类:

    package com.koushik.restapis.intercepter;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletResponse;
    
    
    public class RestCorsFilter implements Filter {
    
        @Override
        public void destroy() {
        enter code here
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
        
            HttpServletResponse resp = (HttpServletResponse) response;
            resp.addHeader("Access-Control-Allow-Origin", "*");
            resp.addHeader("Access-Control-Allow-Headers", "*");
            resp.addHeader("Access-Control-Allow-Methods", "*");
            
            chain.doFilter(request, resp);
            
        }
    
        @Override
        public void init(FilterConfig arg0) throws ServletException {
            
        }
    
    }
    

    web.xml

      <filter>
        <filter-name>RestCorsFilter</filter-name>
        <filter-class>com.koushik.restapis.RestCorsFilter</filter-class>
      </filter>
        <filter-mapping>
        <filter-name>RestCorsFilter</filter-name>
        <url-pattern>/apis/*</url-pattern>
      </filter-mapping>
    

    【讨论】:

      【解决方案8】:

      @Joel Pearson 的回答有所帮助,但对于像我这样的 JAX-RS 新手并通过配置 web.xml 在 tomcat 上运行服务的人来说,在创建类并将其放在项目中的任何位置时应该小心。请参阅您在下面为 jersey 指定的包并在那里创建此过滤器类。这种方式对我有用。

      【讨论】:

        【解决方案9】:

        只是为其他回复添加一些内容。 允许 * 有点危险。 可以做的是配置允许来源的数据库(可以是文件)

        那么当请求到达时你可以这样做:

        // this will return you the origin 
        String[] referers = requestContext.getHeaders().get("referer")
        
        // then search in your DB if the origin is allowed
        if (referers != null && referers.lenght == 1 && isAllowedOriging(referers[0])) {
                containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", referers[0]);
                containerResponseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, <HERE PUT YOUR DEDICATED HEADERS>);
                containerResponseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
                containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
                containerResponseContext.getHeaders().add("Access-Control-Max-Age", "1209600");
        }
        

        这样你就不会允许所有人。

        【讨论】:

          猜你喜欢
          • 2015-04-07
          • 2011-10-15
          • 1970-01-01
          • 2014-03-14
          • 2016-12-21
          • 1970-01-01
          • 2023-03-07
          • 2013-07-06
          • 1970-01-01
          相关资源
          最近更新 更多