Jersey异步容器请求筛选器



我有一个Jersey REST API,正在使用ContainerRequestFilter来处理授权。我还在所有端点上使用@ManagedAsync,这样我的API就可以为数千个并发请求提供服务。

我的授权过滤器访问了一个远程服务,但当过滤器运行时,Jersey还没有将当前线程添加到它的内部ExecutorService中,所以我完全失去了异步的好处。

我可以告诉Jersey我希望这个ContainerRequestFilter是异步的吗?

@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
    @Inject
    private AuthorizationService authSvc;
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException
    {
        String authToken = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        // HITS A REMOTE SERVER
        AuthorizationResponse authResponse = authSvc.authorize(authToken);
        if (!authResponse.isAuthorized())
        {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
                    .entity("unauthorized!")
                    .build());
        }
    }
}

这里有一个示例资源:

@Path("/stuff")
@Produces(MediaType.APPLICATION_JSON)
public class StuffResource
{
    @GET
    @Path("/{id}")
    @ManagedAsync
    public void getById(@PathParam("id") long id, @Suspended final AsyncResponse ar)
    {
        Stuff s;
        // HIT THE DATABASE FOR STUFF
        ar.resume(s);
    }
}

更新刚刚收到泽西队队员的回复,这在2.7之前是不可能的。只有资源方法本身被异步调用,而不是过滤器。任何关于继续进行的建议仍然欢迎。

自2.7起,泽西岛没有内置此功能。

如果您有任何过滤器或拦截器来做任何严肃的工作(比如访问远程授权服务),那么@ManagedAsync是无用的。它们可能会在未来增加异步运行过滤器的功能,但目前您只能靠自己了。

更新-还有其他方法。。。

经过漫长而危险的旅程,我找到了一个短期内使用的非常棘手的解决方案。以下是我尝试的内容以及失败/成功的原因。

Guice AOP-失败

我使用Guice进行DI(让Guice注入与Jersey一起工作本身就是一项壮举!),所以我想我可以使用Guice AOP来解决这个问题。尽管Guice注入有效,但不可能让Guice使用Jersey 2创建资源类,因此Guice AOP无法使用资源类方法。如果你拼命想让Guice用Jersey 2创建资源类,不要浪费时间,因为它不起作用。这是一个众所周知的问题。

HK2 AOP-推荐解决方案

HK2最近刚刚发布了一个AOP功能,有关如何使其工作的详细信息,请参阅此问题。

监控-也适用于

这不适合胆小的人,泽西岛的医生们完全不鼓励这样做。您可以注册和ApplicationEventListener并覆盖onRequest以返回一个侦听RESOURCE_METHOD_START并调用身份验证/授权服务的RequestEventListener。这个事件是由@ManagedAsync线程触发的,这就是这里的全部目标。需要注意的是,abortWith方法是无操作的,因此它的工作方式与普通的ContainerRequestFilter不太一样。相反,如果auth失败,您可以抛出一个异常,并注册一个ExceptionMapper来处理您的异常。如果有人大胆尝试一下,请告诉我,我会发布代码。

我不确定这是否是您想要的,但是,您研究过Spring的OncePerRequestFilter吗?我目前正在将其用于我的授权层,在授权层中,每个请求都会经过一些过滤器,这些过滤器根据我的过滤器如何映射到URL来扩展这个OncePerRequestFilter。以下是我如何使用它的快速概述:

Dropwizard 中资源的身份验证/授权

我不太清楚这些过滤器的异步调度部分,但我希望这个链接至少能为你想要实现的目标提供一些启示!

我们使用Spring安全性进行身份验证/授权。我使用空路径的子资源定位器解决了这个问题,如下所示:

@Path("/customers")
public class CustomerResource {
    @Inject
    private CustomerService customerService;
    @Path("")
    public CustomerSubResource delegate() {
        final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        return new CustomerSubResource(auth);
    }
    public class CustomerSubResource {
        private final Authentication auth;           
        public CustomerSubResource(final Authentication auth) {
            this.auth = auth;
        }
        @POST
        @Path("")
        @Produces(MediaType.APPLICATION_JSON)
        @Consumes(MediaType.APPLICATION_JSON)
        @ManagedAsync
        public void createCustomer(final Customer customer, @Suspended final AsyncResponse response) {
            // Stash the Spring security context into the Jersey-managed thread
            SecurityContextHolder.getContext().setAuthentication(this.auth);
            // Invoke service method requiring pre-authorization
            final Customer newCustomer = customerService.createCustomer(customer);
            // Resume the response
            response.resume(newCustomer);
        }
    }
}

最新更新