【问题标题】:Ajax request with JAX-RS/RESTEasy implementing CORS使用 JAX-RS/RESTEasy 实现 CORS 的 Ajax 请求
【发布时间】:2013-01-13 09:14:26
【问题描述】:

我有两台服务器(Apache 和 JBoss AS7),我需要向客户端提供对所有 http 方法的访问。所有这些请求都必须通过 ajax 发送。 客户端代码示例:

$.ajax({
      type: "get",
      url: "http://localhost:9080/myproject/services/mobile/list",
      crossDomain: true,
      cache: false,
      dataType: "json",
      success: function(response) {
        console.log(response);
      },
      error: function (jqXHR, textStatus, errorThrown) {
        console.log(textStatus);
        console.log(jqXHR.responseText);
        console.log(errorThrown);
        }
    });

在 JBoss AS7 中,我使用的是 RESTEasy,实现 CORS 如下:

@Path("/mobile")
@Provider
@ServerInterceptor
public class GroupMobile implements MessageBodyWriterInterceptor {

@Inject
private GroupDAO groupDAO;

@GET
@Path("/list")
@Produces(MediaType.APPLICATION_JSON)
public List<Group> getGroups() {
    return groupDAO.listAll();
}

@Override
public void write(MessageBodyWriterContext context) throws IOException,
        WebApplicationException {
    context.getHeaders().add("Access-Control-Allow-Origin", "*");
    context.proceed();
}

@OPTIONS
@Path("/{path:.*}")
public Response handleCORSRequest(
        @HeaderParam("Access-Control-Request-Method") final String requestMethod,
        @HeaderParam("Access-Control-Request-Headers") final String requestHeaders) {
    final ResponseBuilder retValue = Response.ok();

    if (requestHeaders != null)
        retValue.header("Access-Control-Allow-Headers", requestHeaders);

    if (requestMethod != null)
        retValue.header("Access-Control-Allow-Methods", requestMethod);

    retValue.header("Access-Control-Allow-Origin", "*");

    return retValue.build();
}
}

web.xml 和 beans.xml 是空文件。 当我访问 MyIP:8080 (Apache) 时,我收到错误消息:

XMLHttpRequest cannot load http://localhost:9080/myproject/services/mobile/list?_=1359480354190. Origin http://MyIP:8080 is not allowed by Access-Control-Allow-Origin.

有人知道怎么回事吗?

【问题讨论】:

标签: ajax json jax-rs resteasy cors


【解决方案1】:

我最近遇到了同样的问题并添加了对我有用的解决方案:

以下是我所做的:

  • 我创建了一个扩展 javax.ws.rs.core.Application 的类,并向其中添加了一个 Cors 过滤器。

在 CORS 过滤器中,我添加了 corsFilter.getAllowedOrigins().add("http://localhost:4200");

基本上,您应该添加要允许跨域资源共享的 URL。 Ans 你也可以使用"*" 而不是任何特定的 URL 来允许任何 URL。

public class RestApplication
    extends Application
{
    private Set<Object> singletons = new HashSet<Object>();

    public MessageApplication()
    {
        singletons.add(new CalculatorService()); //CalculatorService is your specific service you want to add/use.
        CorsFilter corsFilter = new CorsFilter();
        // To allow all origins for CORS add following, otherwise add only specific urls.
        // corsFilter.getAllowedOrigins().add("*");
        System.out.println("To only allow restrcited urls ");
        corsFilter.getAllowedOrigins().add("http://localhost:4200");
        singletons = new LinkedHashSet<Object>();
        singletons.add(corsFilter);
    }

    @Override
    public Set<Object> getSingletons()
    {
        return singletons;
    }
}
  • 这是我的 web.xml:
<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>Restful Web Application</display-name>

    <!-- Auto scan rest service -->
    <context-param>
        <param-name>resteasy.scan</param-name>
        <param-value>true</param-value>
    </context-param>

    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/rest</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>resteasy-servlet</servlet-name>
        <servlet-class>
            org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
        </servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.app.RestApplication</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

</web-app>

有一些您可能不需要或更改的自定义更改,例如:

    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>resteasy.scan</param-name>
        <param-value>true</param-value>
    </context-param>

    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/rest</param-value>
    </context-param>

最重要的代码当我遇到这个问题时,我没有添加我的扩展 javax.ws.rs.Application 的类,即 RestApplication 到 @987654328 的 init-param @

   <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>com.app.RestApplication</param-value>
    </init-param>

因此我的过滤器无法执行,因此应用程序不允许来自指定 URL 的 CORS。

PS:我使用的是 RestEasy 版本:3.12.1.Final

【讨论】:

    【解决方案2】:

    最新的resteasy(3.0.9-Final)包含一个实用程序类org.jboss.resteasy.plugins.interceptors.CorsFilter

    您可以将 CorsFilter 对象添加到应用程序的单例中 对象集,或将其添加到 ResteasyDeployment 中的 ProviderFactory 直接。

    以下是示例应用程序类:

    import java.util.HashSet;
    import java.util.Set;
    import javax.ws.rs.ApplicationPath;
    
    import org.jboss.resteasy.plugins.interceptors.CorsFilter;
    
    
    @ApplicationPath("/api")
    public class RestApplication extends javax.ws.rs.core.Application {
        Set<Object> singletons;
    
        @Override
        public Set<Class<?>> getClasses() {
            HashSet<Class<?>> clazzes = new HashSet<>();
            clazzes.add(VersionService.class);
            return clazzes;
        }
    
        @Override
        public Set<Object> getSingletons() {
            if (singletons == null) {
                CorsFilter corsFilter = new CorsFilter();
                corsFilter.getAllowedOrigins().add("*");
    
                singletons = new LinkedHashSet<Object>();
                singletons.add(corsFilter);
            }
            return singletons;
        }
    }
    

    【讨论】:

    • 对我不起作用。当我将 getSingletons() 方法添加到我的应用程序时,我无法调用我的任何休息端点,它们根本停止工作。删除 getSingletons 方法后,我可以再次调用端点。
    • 这是我在之前评论中描述的问题的解决方案:stackoverflow.com/questions/29388937/…
    • 我遇到了同样的问题,Tomcat 忽略了带注释的 CorsFilter 类,我必须在应用程序中添加 cors 过滤器,然后像您一样将其添加到单例中。谢谢
    【解决方案3】:

    好吧,我已经实现了一个小解决方案,首先我在我的项目Web中做一个拦截器 我创建了一个名为“CORSInterceptor”的类。

    import org.jboss.resteasy.annotations.interception.ServerInterceptor;
    import org.jboss.resteasy.core.ResourceMethod;
    import org.jboss.resteasy.core.ServerResponse;
    import org.jboss.resteasy.spi.Failure;
    import org.jboss.resteasy.spi.HttpRequest;
    import org.jboss.resteasy.spi.interception.MessageBodyWriterContext;
    import org.jboss.resteasy.spi.interception.MessageBodyWriterInterceptor;
    import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
    import javax.ws.rs.WebApplicationException;
    import javax.ws.rs.ext.Provider;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
    
    @Provider
    @ServerInterceptor
    public class CorsInterceptor implements PreProcessInterceptor, MessageBodyWriterInterceptor {
    
    /**
         * The Origin header set by the browser at each request.
         */
        private static final String ORIGIN = "Origin";
    
    
     /**
         * The Access-Control-Allow-Origin header indicates which origin a resource it is specified for can be
         * shared with. ABNF: Access-Control-Allow-Origin = "Access-Control-Allow-Origin" ":" source origin string | "*"
         */
        private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
    
    
       //
        private static final ThreadLocal<String> REQUEST_ORIGIN = new ThreadLocal<String>();
        //
        private final Set<String> allowedOrigins;
    
    
     public CorsInterceptor(){
       this.allowedOrigins = new HashSet<String>();
       this.allowedOrigins.add("*");
     }
    
       @Override
        public ServerResponse preProcess(HttpRequest request, ResourceMethod method) throws Failure, WebApplicationException {
            if (!allowedOrigins.isEmpty()) {
                REQUEST_ORIGIN.set(request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN));
            }
            return null;
        }
    
    
    @Override
    public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException {
        if (!allowedOrigins.isEmpty() && (allowedOrigins.contains(REQUEST_ORIGIN.get()) || allowedOrigins.contains("*"))) {
            context.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN, REQUEST_ORIGIN.get());
          }
        context.proceed();
        }
    }
    

    Web.xml文件中,我添加了

    <context-param>
        <param-name>resteasy.providers</param-name>
        <param-value><package>.CorsInterceptor</param-value>
    </context-param>
    

    :类的统一。

    请求我使用过的 JQuery。

    $.ajax({
        type: 'GET',
        dataType: "json", 
    
        crossDomain : true,
    
        cache:false, 
        url: "http://localhost:12005/ProyectoWebServices/ws/servicioTest",
        success: function (responseData, textStatus, jqXHR) {
           alert("Successfull: "+responseData);
        },
        error: function (responseData, textStatus, errorThrown) {
            alert("Failed: "+responseData);
        }
    });
    

    对我来说效果很好。希望对你有帮助。

    【讨论】:

      【解决方案4】:

      听起来这个问题与https://issues.jboss.org/browse/RESTEASY-878 有关。您可能无法使用MessageBodyWriterInterceptor 捕获 CORS 预检请求。尝试改用 servlet 过滤器 (@WebFilter)。

      【讨论】:

        【解决方案5】:

        您遇到的问题是您正在尝试执行跨站点脚本。您在http://MyIP:8080 访问了该页面,因此浏览器阻止您访问该域之外的资源。这是非常特定于浏览器的,基于浏览器的解决方法都会有所不同(您可以在 Chrome 中全局禁用安全性,并在 IE 中基于每个站点禁用安全性)。

        如果您将页面加载为http://localhost:8080,那么它应该允许您访问查询。或者,您可以实现一个代理来转发请求。

        【讨论】:

        • @Leo 如果可以解决问题,请投票或将答案标记为正确。干杯。
        • 感谢马克的快速回复!如何实现代理?如果我使用另一个带有 CXF 而不是 RESTEasy 的 AS(例如 Tomcat),它可以在没有代理的情况下工作。
        • CXF 完全有可能实现一个代理来做这样的事情。或者,tomcat 在这里有代理wiki.apache.org/tomcat/ServletProxy 更好的解决方案是修复地址,这样您就不会执行任何跨站点脚本。设置代理很麻烦,如果做得不好也会出现安全漏洞。
        • 我同意!但我必须这样做才能使用该架构:访问 Apache 的客户端使用 JBoss AS7 执行跨站点脚本(使用 JAX-RS/RESTEasy)。你有一些关于我如何实现代理的参考吗?
        • 等一下。用户明确表示他正在使用 CORS。所以跨域请求应该可以工作。您是否有在请求/响应中发送的标头示例?
        猜你喜欢
        • 1970-01-01
        • 2014-05-18
        • 1970-01-01
        • 2018-12-08
        • 1970-01-01
        • 2017-05-05
        • 2016-07-23
        • 2014-02-03
        • 1970-01-01
        相关资源
        最近更新 更多