如何在注入JAX-RS Web服务的CDI bean中获取HTTP请求标头?

时间:2015-10-07 12:27:06

标签: java jax-rs httprequest cdi

我有这样的网络服务:

@Path("/projects")
public class Projects {
    [...]

    @Inject
    CurrentRequest current;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("{id}")
    public Response getProject(@PathParam("id") String id) {
        if (current.isUserAuthenticated()) {
            [...do something...]
        } else {
            [...produce an error...]
        }
    }
}

一个带有auth checker方法的CDI bean,如下所示:

@RequestScoped
public class CurrentRequest {

    public boolean isUserAuthenticated() {
        [...do some header checking...]
    }
}

我的问题是,我不能为我的生活抓住CurrentRequest内的HTTP标头。我尝试注入HttpServletRequest,但它没有初始化。我尝试使用@Context,同样的事情。显然FacesContext.getCurrentInstance()不起作用,因为没有FacesContext。

我看到this question基本上是在问同样的事情,但没有引起太多关注。

我目前的做法是在@Context HttpServletRequest request内使用Projects并将其作为参数传递给current.isUserAuthenticated(request)。但那感觉很糟糕。 CDI bean不应该知道它自己的请求吗?

我错过了什么?

2 个答案:

答案 0 :(得分:5)

提取HTTP标头

您不需要JAX-RS端点中的HttpServletRequest来获取请求中的HTTP标头。相反,您可以注入HttpHeaders

@Context
HttpHeaders httpHeaders;

然后,您可以使用HttpHeaders API获取标头值:

如果您需要标准HTTP标头的值,请考虑使用constants available in the HttpHeaders API

// Get the value of the Authorization header
String authorizationHeader = httpHeaders.getHeaderString(HttpHeaders.AUTHORIZATION);

使用过滤器

由于您正在执行身份验证和/或授权,因此我建议您使用过滤器,这样您就可以保持REST端点的精简并专注于业务逻辑。

为了将过滤器绑定到REST端点,JAX-RS提供了元注释@NameBinding,可以按如下方式使用:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

@Secured注释将用于修饰过滤器类,它实现ContainerRequestFilter,允许您处理请求。

ContainerRequestContext可帮助您从HTTP请求中提取信息(有关详细信息,请查看ContainerRequestContext API):

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // Use the ContainerRequestContext to extract information from the HTTP request
        // Information such as the URI, headers and HTTP entity are available
    }
}

如果用户未经过身份验证/授权,ContainerRequestFilter#filter()方法是中止请求的好地方。为此,您可以使用ContainerRequestContext#abortWith()或抛出异常。

@Provider注释标记了在提供程序扫描阶段JAX-RS运行时应该可以发现的扩展接口的实现。

要将过滤器绑定到端点方法或类,请使用上面创建的@Secured注释对其进行注释。对于注释的方法和/或类,将执行过滤器。

@Path("/")
public class MyEndpoint {

    @GET
    @Path("{id}")
    @Produces("application/json")
    public Response myUnsecuredMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // The security filter won't be executed before invoking this method
        ...
    }

    @DELETE
    @Secured
    @Path("{id}")
    @Produces("application/json")
    public Response mySecuredMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured
        // The security filter will be executed before invoking this method
        ...
    }
}

在上面的示例中,安全过滤器仅针对mySecuredMethod(Long)执行,因为它使用@Secured进行了注释。

您可以拥有REST端点所需的尽可能多的过滤器。要确保过滤器的执行顺序,请使用@Priority对其进行注释。

强烈建议使用Priorities类中定义的值之一(将使用以下顺序):

如果您的过滤器未使用@Priority进行注释,则过滤器将以USER优先级执行。

其他信息

你可能会发现这个answer很有用。

答案 1 :(得分:1)

根据JAX-RS实现和您使用的服务器,您可能需要一些依赖项来提供CDIJAX-RS之间的集成:

Jersey Ext Cdi1x

<dependency>
    <groupId>org.glassfish.jersey.ext.cdi</groupId>
    <artifactId>jersey-cdi1x</artifactId>
    <version>2.22.1</version>
</dependency>

RESTEasy CDI Integration Module

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-cdi</artifactId>
    <version>3.0.13.Final</version>
</dependency>

Apache CXF CDI Integration

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-integration-cdi</artifactId>
    <version>3.1.3</version>
</dependency>