我有这样的网络服务:
@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不应该知道它自己的请求吗?
我错过了什么?
答案 0 :(得分:5)
您不需要JAX-RS端点中的HttpServletRequest
来获取请求中的HTTP标头。相反,您可以注入HttpHeaders
:
@Context
HttpHeaders httpHeaders;
然后,您可以使用HttpHeaders
API获取标头值:
HttpHeaders#getHeaderString(String)
HttpHeaders#getRequestHeaders()
HttpHeaders#getHeaderString(String)
如果您需要标准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实现和您使用的服务器,您可能需要一些依赖项来提供CDI和JAX-RS之间的集成:
<dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-cdi</artifactId>
<version>3.0.13.Final</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-integration-cdi</artifactId>
<version>3.1.3</version>
</dependency>