如何在JAX-RS Web服务上启用跨域请求?


69

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

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

我正在使用WildFly 8,JavaEE 7


2
您可以在此处了解有关CORS的更多信息:html5rocks.com/en/tutorials/cors您可以在此处了解如何向JAX-RS添加CORS支持:cxf.apache.org/docs/jax-rs-cors.html
monsur

这很有用,但我一直在寻找一种无需修改客户端代码的解决方案。
奈里2014年

1
您的问题要求在服务器端进行更改。CORS的大部分更改都是在服务器端完成的。客户端中需要的更改很少(如果有的话)。
monsur 2014年

Answers:


112

我在想同样的事情,因此经过一番研究,我发现最简单的方法就是使用JAX-RSContainerResponseFilter添加相关的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运行时使用过滤器,而没有注释则什么也没有发生。

ContainerResponseFilterJersey示例中得到了有关使用的想法。


8
是的,我使用了您在此处发布的相同解决方案。但是我相信,通过允许每个跨域连接,我们正在制造一个安全问题。
奈里2014年

1
是的,如果您的REST Web服务使用cookie来保护它们,那是真的。但是,如果您只是尝试制作一些公共的REST Web服务,那么就没有安全问题。您没有在问题中提到是否只需要1个域即可工作,例如,services.yourdomain或每个域。
乔尔·皮尔森

1
@乔尔·皮尔森同意。我通过谷歌搜索“在jax-rs中启用CORS”遇到了这个问题。只是认为这可能对同样进入此页面的其他人有所帮助。但是,已接受的答案似乎并未暗示任何针对野蝇或jee7的东西。可能是我丢失了链接吗?这是在码头+球衣上对我有用的解决方案,并感谢您发布它。
Ashok,2015年

2
Access-Control-Allow-Credentials: true此行实际上与之冲突,"Access-Control-Allow-Origin", "*"因为这可能是安全问题,或者(至少在Chrome中是)被拒绝。如果要允许凭据,则需要指定特定的来源,而不是通配符。
Stijn de Witt

1
首先获取请求的原点:String origin = requestContext.getHeaderString("origin");,然后(当时origin !== null)使用该原点,即通配符:cres.getHeaders().add("Access-Control-Allow-Origin", origin);。如果您使用的API是用于身份验证的Cookie(例如,使用容器管理的身份验证时),请在设置CORS标头之前对照允许的来源白名单检查来源。
Stijn de Witt

17

我遇到了类似的问题,并尝试使用@Alex Petty的解决方案,但除了必须在类中的每个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用户界面中却无法呈现同样的效果
Debaprasad Jana 2015年

那是不对的。您必须使用过滤器。当您要根据特定条件过滤和/或修改请求时,请使用“过滤器”。当您想要控制,预处理和/或后处理请求时,请使用Servlet。docs.oracle.com/javaee/6/tutorial/doc/bnagb.html
max_dev

我刚刚添加了这个.header(“ Access-Control-Allow-Origin”,“ *”),它起作用了
Pini Cheyni '17

12

我发现了一种更简单的(特定于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版本?),并且您的解决方案工作得很好。
Guildenstern70年

这种方法仅适用于GET,当尝试从客户端进行POST时,会发生相同的错误,但我仍试图找出解决方法,因为我的“来源”会一直变化。
Francisco Souza 2015年

1
这永远都行不通,这是错误的。CORS首先调用HTTP方法“ OPTIONS”,然后再调用“ GET”,“ POST”或其他方法。标头条目必须在“ OPTIONS”方法中定义。如果不是,浏览器将不会接受。HTTP请求过滤是最好的方法。另一件事,如果您正在使用像Apache这样的严肃的HTTP服务器,则“ Access-Control-Allow-Origin”,“ *”条目将是稀疏的,而浏览器将不允许它。您必须专门添加授权DNS,例如“ Access-Control-Allow-Origin:my.domain.com ”。
jbrios777 '16

但是,如果您有100多种方法,那么更好的方法是使用过滤器
ACV

用适当的解释完整的回答到飞行前的请求已被@保罗Samsotha给出,这里是链接- stackoverflow.com/a/28067653/3257644
马赫什

11

我很幸运通过使用此lib为我的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上进行更改以使其正确?
Ayyoub 2015年

2
我尝试了此操作,但看不到此添加到标题中。
周杰伦

7

没有其他答案对我有用,但这确实做到了:

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请求。
拉努卡

3

您也可以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;
 }

}

1

解决方案为此在响应上添加了一些标头。在春季或春季靴子中有注释,但是在较旧的系统中,可能没有注释。您可以通过添加过滤器来解决。

过滤器类别:

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>

0

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


0

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

然后,当请求到达时,您可以执行以下操作:

// 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");
}

这样一来,您就不会允许所有人。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.