使用 JAX-RS/RESTEasy 实现 CORS 的 Ajax 请求

Posted

技术标签:

【中文标题】使用 JAX-RS/RESTEasy 实现 CORS 的 Ajax 请求【英文标题】:Ajax request with JAX-RS/RESTEasy implementing CORS 【发布时间】: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.

有人知道怎么回事吗?

【问题讨论】:

this article 帮助了我。不过我用的是球衣。 【参考方案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 方法后,我可以再次调用端点。 这是我在之前评论中描述的问题的解决方案:***.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。所以跨域请求应该可以工作。您是否有在请求/响应中发送的标头示例?

以上是关于使用 JAX-RS/RESTEasy 实现 CORS 的 Ajax 请求的主要内容,如果未能解决你的问题,请参考以下文章

使用JAX-RS resteasy和ContainerRequestFilter / ContainerResponseFilter记录请求

将 Web 代理与 Java 8 JAX-RS RESTEasy 客户端一起使用

如何将对象从 ContainerRequestFilter 传递到资源

R使用笔记:相关系数:cor.test();corr.test();rcorr()

R语言使用cov函数计算矩阵或者dataframe数据变量之间的协方差cor函数计算相关性cor函数通过method参数指定相关性相关性计算方法Pearson,Spearman, Kendall

R语言使用cov函数计算矩阵或者dataframe数据变量之间的协方差cor函数计算相关性cor函数通过method参数指定相关性相关性计算方法Pearson,Spearman, Kendall