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

Ant*_*s42 5 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...]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@RequestScoped
public class CurrentRequest {

    public boolean isUserAuthenticated() {
        [...do some header checking...]
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,我不能为我的生活从内部抓住HTTP标头CurrentRequest.我试过注射HttpServletRequest,但它没有初始化.我试过用@Context同样的东西.显然FacesContext.getCurrentInstance()不起作用,因为没有FacesContext.

我看到这个问题基本上是在问同样的问题,但是没有得到太多的关注.

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

我错过了什么?

cas*_*lin 5

提取HTTP标头

您不需要HttpServletRequest在JAX-RS端点中从请求中获取HTTP标头.相反,你可以注入HttpHeaders:

@Context
HttpHeaders httpHeaders;
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用HttpHeadersAPI来获取标头值:

如果需要标准HTTP标头的值,请考虑使用API中提供HttpHeaders常量:

// Get the value of the Authorization header
String authorizationHeader = httpHeaders.getHeaderString(HttpHeaders.AUTHORIZATION);
Run Code Online (Sandbox Code Playgroud)

使用过滤器

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

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

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }
Run Code Online (Sandbox Code Playgroud)

@Secured注释将被用来装饰一个过滤器类,它实现ContainerRequestFilter,让您处理请求.

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

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

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
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,安全过滤器将仅执行,mySecuredMethod(Long)因为它带有注释@Secured.

您可以根据需要为REST端点提供尽可能多的过滤器.要确保过滤器的执行顺序,请使用@Priority.

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

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

附加信息

你可能会发现这个答案很有用.