【问题标题】:Authentication/authorization in JAX-RS using interceptors and injection使用拦截器和注入在 JAX-RS 中进行身份验证/授权
【发布时间】:2014-05-26 22:48:39
【问题描述】:

我正在使用 WildFly 8 在 JavaEE 7 中开发一个新应用程序。我正在使用 JAX-RS 为远程应用程序提供 RESTful 服务接口。

可以使用@Context 注解将HttpHeaders 对象之类的东西注入到资源方法参数中。由于该对象基于请求参数(当然是 HTTP 标头),我想出了创建自己的可注入 User 对象的想法,该对象是基于请求中存在的有效令牌(类似于OAuth 访问令牌)。

所以,我想实现这样的目标:

@Path("/resources")
public class MyResource {

    @Path("/{id}")
    @GET
    public Response getById(@Context User user, @PathParam("id") long id) {
        ...
    }

}

其中 User 是根据请求参数创建的可注入对象,例如可通过 HttpHeaders 对象访问的对象。当然,如果由于任何原因无法创建 User 对象,提供者也可以抛出异常并返回 HTTP 错误响应。

现在,我的问题是:

  1. 这是一个好的设计吗?如果没有,我还有什么更好的选择?
  2. 我怎样才能做到这一点?我不在乎我的解决方案是否特定于 JAX-RS 并使用特定于 WildFly/RestEasy 的内部结构,但如果存在便携式解决方案,我绝对更喜欢便携式解决方案。

谢谢

【问题讨论】:

    标签: rest jakarta-ee jax-rs restful-authentication wildfly


    【解决方案1】:

    在我看来,只要您不尝试使用此用户对象构建类似会话的东西,这种方法就有效。

    作为answered here,您可以使用@Context@Provider,但这并不是您想要的。 使用Resteasy Dispatcher 可以根据@Context 直接注入一个类。 但是在这里你必须注册应该注入的对象。我认为这对于请求范围的参数没有意义。 你可以做的是注入这样的提供者:

    // Constructor of your JAX-RS Application
    public RestApplication(@Context Dispatcher dispatcher) {
        dispatcher.getDefaultContextObjects().put(UserProvider.class, new UserProvider());
    }
    
    // a resource
    public Response getById(@Context UserProvider userProvider) {
        User user = userProvider.get();
    }
    

    解决问题的其他方法:

    1. 注册一个WebFilter,验证用户,包装ServletRequest并覆盖getUserPrincipal。然后,您可以从注入的 HttpServletRequest 访问 UserPrincipal。
    2. 实现一个 JAX-RS 拦截器,它实现了ContainerRequestFilter。将 ContainerRequestContext.html#setSecurityContext 与 UserPrincipal 一起使用,并将 SecurityContext 作为 ResourceMethod-Parameter 注入。
    3. 实现一个CDI-Interceptor,它会更新您的方法参数。
    4. 实现一个类 which produces 您的用户并通过 CDI 注入它。

    我将示例推送到github

    【讨论】:

      【解决方案2】:

      虽然 lefloh 的回答绝对正确,但我想详细说明他认为最方便的第二种方法。

      您将创建实现ContainerRequestFilter 的拦截器(过滤器)。

      import org.apache.commons.lang.StringUtils;
      import org.jboss.resteasy.spi.ResteasyProviderFactory;
      
      import javax.ws.rs.container.ContainerRequestContext;
      import javax.ws.rs.container.ContainerRequestFilter;
      import javax.ws.rs.core.MediaType;
      import javax.ws.rs.core.Response;
      import javax.ws.rs.ext.Provider;
      import java.io.IOException;
      
      @Provider
      public class UserProvider implements ContainerRequestFilter {
      
          @Override
          public void filter(ContainerRequestContext containerRequestContext) throws IOException {
              String userId = containerRequestContext.getHeaders().getFirst("User-Id");
              if (StringUtils.isEmpty(userId)) {
                  Response response = Response
                          .status(Response.Status.BAD_REQUEST)
                          .type(MediaType.TEXT_PLAIN_TYPE)
                          .entity("User-Id header is missing.")
                          .build();
                  containerRequestContext.abortWith(response);
                  return;
              }
      
              //do your logic to obtain the User object by userId
      
              ResteasyProviderFactory.pushContext(User.class, user);
          }
      }
      

      您需要通过自动发现 set up in web.xml 来注册它

      <context-param>
          <param-name>resteasy.scan</param-name>
          <param-value>true</param-value>
      </context-param>
      

      或在您的应用启动中

      ResteasyProviderFactory.getInstance().registerProvider(UserProvider.class);
      

      由于获取用户背后的所有逻辑都在 UserProvider 中,因此您可以像这样使用它

      @Path("/orders")
      @GET
      public List<Order> getOrders(@Context User user) {
          return user.getOrders();
      }
      

      【讨论】:

      • 非常感谢。你有什么想法可以跨 JEE 容器移植吗?
      • resteasy.scan 已完全弃用。它只是被忽略了。
      猜你喜欢
      • 2012-02-10
      • 2013-07-30
      • 2012-07-05
      • 1970-01-01
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      相关资源
      最近更新 更多