ContainerResponseFilter

ContainerResponseFilter

本文介绍了如何使用 JAX-RS 和 Jersey 处理 CORS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个 java 脚本客户端应用程序,在服务器端我需要处理 CORS,我用 JERSEY 用 JAX-RS 编写的所有服务.我的代码:

@CrossOriginResourceSharing(allowAllOrigins = true)@得到@Path("/readOthersCalendar")@Produces("应用程序/json")公共响应 readOthersCalendar(String dataJson) 抛出异常 {//我的代码.由 gimbal2 编辑以修复格式返回 Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build();}

截至目前,我收到错误请求的资源上不存在Access-Control-Allow-Origin"标头.Origin 'http://localhost:8080' 因此不允许访问."

请帮我解决这个问题.

谢谢&问候菩提佛

解决方案

对于 Jersey,要处理 CORS,您只需使用 ContainerResponseFilter.Jersey 1.x 和 2.x 的 ContainerResponseFilter 有点不同.由于您没有提到您使用的是哪个版本,我将同时发布两个版本.确保使用正确的.

球衣 2.x

import java.io.IOException;导入 javax.ws.rs.container.ContainerRequestContext;导入 javax.ws.rs.container.ContainerResponseContext;导入 javax.ws.rs.container.ContainerResponseFilter;@提供者公共类 CORSFilter 实现了 ContainerResponseFilter {@覆盖公共无效过滤器(ContainerRequestContext 请求,ContainerResponseContext 响应)抛出 IOException {response.getHeaders().add("Access-Control-Allow-Origin", "*");response.getHeaders().add("Access-Control-Allow-Headers",CSRF-Token,X-Requested-By,授权,内容类型");response.getHeaders().add(Access-Control-Allow-Credentials", true");response.getHeaders().add(Access-Control-Allow-Methods",获取,发布,放置,删除,选项,头部");}}

如果您使用包扫描来发现提供者和资源,@Provider 注释应该为您处理配置.如果没有,那么您需要使用 ResourceConfigApplication 子类显式注册它.

使用 ResourceConfig 显式注册过滤器的示例代码:

final ResourceConfig resourceConfig = new ResourceConfig();resourceConfig.register(new CORSFilter());final 最终 URI uri = ...;最终 HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);

对于 Jersey 2.x,如果您在注册此过滤器时遇到问题,这里有一些资源可能会有所帮助

球衣 1.x

import com.sun.jersey.spi.container.ContainerRequest;进口 com.sun.jersey.spi.container.ContainerResponse;进口 com.sun.jersey.spi.container.ContainerResponseFilter;@提供者公共类 CORSFilter 实现了 ContainerResponseFilter {@覆盖公共容器响应过滤器(ContainerRequest 请求,ContainerResponse 响应) {response.getHttpHeaders().add(Access-Control-Allow-Origin", *");response.getHttpHeaders().add("Access-Control-Allow-Headers",CSRF-Token,X-Requested-By,授权,内容类型");response.getHttpHeaders().add(Access-Control-Allow-Credentials", true");response.getHttpHeaders().add(Access-Control-Allow-Methods",获取,发布,放置,删除,选项,头部");返回响应;}}

web.xml 配置,可以使用

<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name><param-value>com.yourpackage.CORSFilter</param-value></init-param>

或者ResourceConfig你可以做

resourceConfig.getContainerResponseFilters().add(new CORSFilter());

或者使用 @Provider 注释进行包扫描.


编辑

请注意上面的例子可以改进.您将需要更多地了解 CORS 的工作原理.请参阅此处.一方面,您将获得所有响应的标题.这可能是不可取的.您可能只需要处理预检(或选项).如果你想看到一个更好实现的 CORS 过滤器,你可以查看 RESTeasy CorsFilter


更新

所以我决定添加一个更正确的实现.上面的实现是惰性的,并将所有 CORS 标头添加到所有请求中.另一个错误是它只是一个响应过滤器,请求仍在处理中.这意味着当 preflight 请求进来时,它是一个 OPTIONS 请求,不会有 OPTIONS 方法被实现,所以我们会得到一个 405 响应,这是不正确的.

这是应该的工作方式.所以有两种类型的 CORS 请求:简单请求和预检请求.对于简单的请求,浏览器将发送实际请求并添加 Origin 请求标头.浏览器期望响应具有 Access-Control-Allow-Origin 标头,表示允许来自 Origin 标头的来源.为了使其被视为简单请求",它必须满足以下条件:

  • 采用以下方法之一:
    • 获取
    • 头部
    • 发布
  • 除了浏览器自动设置的标头外,请求可能只包含以下手动设置的标头:
    • 接受
    • 接受语言
    • 内容语言
    • 内容类型
    • DPR
    • 保存数据
    • 视口宽度
    • 宽度
  • Content-Type 标头的唯一允许值是:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

如果请求不满足所有这三个条件,则会发出预检请求.这是向服务器发出的 OPTIONS 请求,在发出实际请求之前.它将包含不同的 Access-Control-XX-XX 标头,服务器应该使用自己的 CORS 响应标头响应这些标头.以下是匹配的标题:

请求头响应头
起源访问控制允许来源
访问控制请求头访问控制允许标题
访问控制请求方法访问控制允许方法
XHR.withCredentials访问控制允许凭据

  • 使用 Origin 请求头,值将是源服务器域,响应 Access-Control-Allow-Origin 应该是相同的address 或 * 指定允许所有来源.

  • 如果客户端尝试手动设置不在上述列表中的任何标头,则浏览器将设置Access-Control-Request-Headers标头,其值为以下列表客户端尝试设置的所有标头.服务器应该用 Access-Control-Allow-Headers 响应头响应,其值是它允许的头列表.

  • 浏览器也会设置Access-Control-Request-Method请求头,值为请求的HTTP方法.服务器应该用 Access-Control-Allow-Methods 响应头响应,其值是它允许的方法列表.

  • 如果客户端使用 XHR.withCredentials,那么服务器应该使用 Access-Control-Allow-Credentials 响应头响应,其值为.在此处阅读更多内容.

综上所述,这里有一个更好的实现.尽管这比上面的实现更好,它仍然不如RESTEasy one 我链接到,因为这个实现仍然允许所有来源.但是这个过滤器在遵守 CORS 规范方面比上面的过滤器做得更好,后者只是将 CORS 响应标头添加到所有请求中.请注意,您可能还需要修改 Access-Control-Allow-Headers 以匹配您的应用程序将允许的标头;在此示例中,您可能希望在列表中添加或删除一些标题.

@Provider@预匹配公共类 CorsFilter 实现了 ContainerRequestFilter、ContainerResponseFilter {/*** ContainerRequestFilter 的方法.*/@覆盖公共无效过滤器(ContainerRequestContext 请求)抛出 IOException {//如果是预检请求,我们将中止请求//一个 200 状态,并且 CORS 头被添加到//下面的响应过滤器方法.如果(isPreflightRequest(请求)){request.abortWith(Response.ok().build());返回;}}/*** 预检请求是一个 OPTIONS 请求* 带有 Origin 标头.*/私有静态布尔值 isPreflightRequest(ContainerRequestContext request) {返回 request.getHeaderString("Origin") != null&&request.getMethod().equalsIgnoreCase(OPTIONS");}/*** ContainerResponseFilter 的方法.*/@覆盖公共无效过滤器(ContainerRequestContext 请求,ContainerResponseContext 响应)抛出 IOException {//如果没有 Origin 头,那么它不是一个//跨源请求.我们什么都不做.if (request.getHeaderString("Origin") == null) {返回;}//如果是预检请求,那么我们添加所有//这里的 CORS 标头.如果(isPreflightRequest(请求)){response.getHeaders().add(Access-Control-Allow-Credentials", true");response.getHeaders().add(Access-Control-Allow-Methods",获取,发布,放置,删除,选项,头部");response.getHeaders().add("Access-Control-Allow-Headers",//任何其他非标准/安全的头文件(见上面的列表)//您希望客户端能够发送到服务器,//把它放在这个列表中.并删除您不想要的那些.X-Requested-With, Authorization,"+接受版本、内容 MD5、CSRF 令牌、内容类型");}//跨源请求可以是简单的请求//或预检请求.我们需要添加这个标题//两种类型的请求.仅预检请求//需要之前添加的标题.response.getHeaders().add("Access-Control-Allow-Origin", "*");}}

要了解有关 CORS 的更多信息,我建议阅读 Cross 上的 MDN 文档-起源资源共享(CORS)

I'm developing a java script client application, in server-side I need to handle CORS, all the services I had written in JAX-RS with JERSEY.My code:

@CrossOriginResourceSharing(allowAllOrigins = true)
@GET
@Path("/readOthersCalendar")
@Produces("application/json")
public Response readOthersCalendar(String dataJson) throws Exception {
     //my code. Edited by gimbal2 to fix formatting
     return Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build();
}

As of now, i'm getting error No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access."

Please assist me with this.

Thanks & RegardsBuddha Puneeth

解决方案

With Jersey, to handle CORS, you can just use a ContainerResponseFilter. The ContainerResponseFilter for Jersey 1.x and 2.x are a bit different. Since you haven't mentioned which version you're using, I'll post both. Make sure you use the correct one.

Jersey 2.x

import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;

@Provider
public class CORSFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext request,
            ContainerResponseContext response) throws IOException {
        response.getHeaders().add("Access-Control-Allow-Origin", "*");
        response.getHeaders().add("Access-Control-Allow-Headers",
                "CSRF-Token, X-Requested-By, Authorization, Content-Type");
        response.getHeaders().add("Access-Control-Allow-Credentials", "true");
        response.getHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
    }
}

If you use package scanning to discover providers and resources, the @Provider annotation should take care of the configuration for you. If not, then you will need to explicitly register it with the ResourceConfig or the Application subclass.

Sample code to explicitly register filter with the ResourceConfig:

final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);

Jersey 1.x

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;

@Provider
public class CORSFilter implements ContainerResponseFilter {
    @Override
    public ContainerResponse filter(ContainerRequest request,
            ContainerResponse response) {

        response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
        response.getHttpHeaders().add("Access-Control-Allow-Headers",
                "CSRF-Token, X-Requested-By, Authorization, Content-Type");
        response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
        response.getHttpHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");

        return response;
    }
}

web.xml configuration, you can use

<init-param>
  <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
  <param-value>com.yourpackage.CORSFilter</param-value>
</init-param>

Or ResourceConfig you can do

resourceConfig.getContainerResponseFilters().add(new CORSFilter());

Or package scanning with the @Provider annotation.


EDIT

Please note that the above example can be improved. You will need to know more about how CORS works. Please see here. For one, you will get the headers for all responses. This may not be desirable. You may just need to handle the preflight (or OPTIONS). If you want to see a better implemented CORS filter, you can check out the source code for the RESTeasy CorsFilter


UPDATE

So I decided to add a more correct implementation. The above implementation is lazy and adds all the CORS headers to all requests. The other mistake is that being that it is only a response filter, the request is still processes. This means that when the preflight request comes in, which is an OPTIONS request, there will be no OPTIONS method implemented, so we will get a 405 response, which is incorrect.

Here's how it should work. So there are two types of CORS requests: simple requests and preflight requests. For a simple request, the browser will send the actual request and add the Origin request header. The browser expects for the response to have the Access-Control-Allow-Origin header, saying that the origin from the Origin header is allowed. In order for it to be considered a "simple request", it must meet the following criteria:

  • Be one of the following method:
    • GET
    • HEAD
    • POST
  • Apart from headers automatically set by the browser, the request may only contain the following manually set headers:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • DPR
    • Save-Data
    • Viewport-Width
    • Width
  • The only allowed values for the Content-Type header are:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

If the request doesn't meet all of these three criteria, a Preflight request is made. This is an OPTIONS request that is made to the server, prior to the actual request being made. It will contain different Access-Control-XX-XX headers, and the server should respond to those headers with its own CORS response headers. Here are the matching headers:

OriginAccess-Control-Allow-Origin
Access-Control-Request-HeadersAccess-Control-Allow-Headers
Access-Control-Request-MethodAccess-Control-Allow-Methods
XHR.withCredentialsAccess-Control-Allow-Credentials

  • With the Origin request header, the value will be the origin server domain, and the response Access-Control-Allow-Origin should be either this same address or * to specify that all origins are allowed.

  • If the client tries to manually set any headers not in the above list, then the browser will set the Access-Control-Request-Headers header, with the value being a list of all the headers the client is trying to set. The server should respond back with a Access-Control-Allow-Headers response header, with the value being a list of headers it allows.

  • The browser will also set the Access-Control-Request-Method request header, with the value being the HTTP method of the request. The server should respond with the Access-Control-Allow-Methods response header, with the value being a list of the methods it allows.

  • If the client uses the XHR.withCredentials, then the server should respond with the Access-Control-Allow-Credentials response header, with a value of true. Read more here.

So with all that said, here is a better implementation. Even though this is better than the above implementation, it is still inferior to the RESTEasy one I linked to, as this implementation still allows all origins. But this filter does a better job of adhering to the CORS spec than the above filter which just adds the CORS response headers to all request. Note that you may also need to modify the Access-Control-Allow-Headers to match the headers that your application will allow; you may want o either add or remove some headers from the list in this example.

@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {

    /**
     * Method for ContainerRequestFilter.
     */
    @Override
    public void filter(ContainerRequestContext request) throws IOException {

        // If it's a preflight request, we abort the request with
        // a 200 status, and the CORS headers are added in the
        // response filter method below.
        if (isPreflightRequest(request)) {
            request.abortWith(Response.ok().build());
            return;
        }
    }

    /**
     * A preflight request is an OPTIONS request
     * with an Origin header.
     */
    private static boolean isPreflightRequest(ContainerRequestContext request) {
        return request.getHeaderString("Origin") != null
                && request.getMethod().equalsIgnoreCase("OPTIONS");
    }

    /**
     * Method for ContainerResponseFilter.
     */
    @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response)
            throws IOException {

        // if there is no Origin header, then it is not a
        // cross origin request. We don't do anything.
        if (request.getHeaderString("Origin") == null) {
            return;
        }

        // If it is a preflight request, then we add all
        // the CORS headers here.
        if (isPreflightRequest(request)) {
            response.getHeaders().add("Access-Control-Allow-Credentials", "true");
            response.getHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
            response.getHeaders().add("Access-Control-Allow-Headers",
                // Whatever other non-standard/safe headers (see list above)
                // you want the client to be able to send to the server,
                // put it in this list. And remove the ones you don't want.
                "X-Requested-With, Authorization, " +
                "Accept-Version, Content-MD5, CSRF-Token, Content-Type");
        }

        // Cross origin requests can be either simple requests
        // or preflight request. We need to add this header
        // to both type of requests. Only preflight requests
        // need the previously added headers.
        response.getHeaders().add("Access-Control-Allow-Origin", "*");
    }
}

To learn more about CORS, I suggest reading the MDN docs on Cross-Origin Resource Sharing (CORS)

这篇关于如何使用 JAX-RS 和 Jersey 处理 CORS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 19:39