Jax-RS - 获取标头值的自定义属性

时间:2014-11-29 23:09:48

标签: java authentication jax-rs

编辑:我刚才意识到,甚至可以在Java中使用自定义属性执行自定义操作吗?或者只是提供信息?

我想在我的Jax-RS服务标头中包含一个身份验证令牌,但我不想在每个请求中添加一个参数来获取标头并检查它:

public Response getUser(@Context HttpHeaders headers) {
    if(authorize(headers.getRequestHeader("token").get(0)) {
        // Do something
    }
}

我更愿意为每个请求添加一个属性(如果可能的话,甚至是类:

@Authorize
public Response getUser() {
    // Do something
}

这样我也可以只将属性添加到我想要的请求中。

如果请求未经授权,我可以覆盖它并返回401。

自定义属性很容易编写,但是如何在不传递属性的情况下获取属性中的标题信息?

注意:我宁愿不使用web.xml。我现在没有,我不喜欢使用它们。我希望在没有xml的情况下保持我的代码干净,我想如果我使用过滤器/ web.xml它将适用于所有调用。如果这是唯一的方法,我会,但我更喜欢使用自定义属性的方法。

3 个答案:

答案 0 :(得分:2)

  

"我想如果我使用过滤器/ web.xml,它将适用于所有来电"

实际上我们可以使用@NameBinding个注释。例如

@NameBinding
@Rentention(RetentionPoilicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Authorize {}

然后只需注释过滤器和要过滤的方法/类。

@Authorize
public Response getUser() {
    // Do something
}

@Provider
@Authorize
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext)
                    throws IOException {

        MultivauledMap<String, String> headers - requestContext.getHeaders();
        ...
        if (!authorized) {
            throw new NotAuthorizedException();
        }
    }
}

请注意@Priority的使用。这个很重要。假设您也想要进行身份验证,因此您可以创建用于身份验证的过滤器。如果您没有设置优先级,则可以先进行过滤。这是不可预测的。如果我们使用@Priority(Priorities.AUTHENTICATION)提供身份验证过滤器,则该过滤器将始终位于@Priority(Priorities.AUTHORIZATION)过滤器之前。

您还需要将此过滤器注册到Application子类(请参阅其他Deployment Options (Jersey, but the Application subclass is portable with implementations)

@ApplicationPath("/api")
public class YourApplication extends Application {
    private Set<Class<?>> classes = new HashSet<>();
    private Set<Object> singletons = new HashSet<>();

    public YourApplication() {
        classes.add(AuthorizationRequestFilter.class);
    }
    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }
    @Override
    public Set<Object> singletons() {
        return singletons;
    }
}

答案 1 :(得分:1)

解决用例的最佳方法是使用名称绑定和过滤器。通过这种方式,您可以使用过滤器来执行授权逻辑,并在未经授权的请求的情况下返回401。

您可以找到更多信息here

Name binding via annotations is only supported as part of the Server API. In name binding, a name-binding annotation is first defined using the @NameBinding meta-annotation:

  @Target({ ElementType.TYPE, ElementType.METHOD })
  @Retention(value = RetentionPolicy.RUNTIME)
  @NameBinding
  public @interface Logged { }

The defined name-binding annotation is then used to decorate a filter or interceptor class (more than one filter or interceptor may be decorated with the same name-binding annotation):
  @Logged
  public class LoggingFilter
          implements ContainerRequestFilter, ContainerResponseFilter {
      ...
  }

At last, the name-binding annotation is applied to the resource method(s) to which the name-bound JAX-RS provider(s) should be bound to:
  @Path("/")
  public class MyResourceClass {
      @GET
      @Produces("text/plain")
      @Path("{name}")
      @Logged
      public String hello(@PathParam("name") String name) {
          return "Hello " + name;
      }
  }

A name-binding annotation may also be attached to a custom JAX-RS Application subclass. In such case a name-bound JAX-RS provider bound by the annotation will be applied to all resource and sub-resource methods in the JAX-RS application:
  @Logged
  @ApplicationPath("myApp")
  public class MyApplication extends javax.ws.rs.core.Application {
      ...
}

答案 2 :(得分:1)

基于peeskillet的回答,概念是正确的,但代码有些错误,这是答案的最终代码。

使用@NameBinding,这有效:

<强> Authorize.java

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Authorize {

}

<强> AuthorizeFilter.java

注意:仍然需要进行实际的令牌授权。这只是检查令牌是否存在。

@Provider
@Authorize
@Priority(Priorities.AUTHORIZATION)
public class AuthorizeFilter implements ContainerRequestFilter
{
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException
    {
        MultivaluedMap<String, String> headers = requestContext.getHeaders();

        String token = headers.getFirst("token");

        if (token == null || token.isEmpty()) {
            Response.ResponseBuilder responseBuilder = Response
                    .status(Response.Status.UNAUTHORIZED)
                    .type(MediaType.APPLICATION_JSON)
                    .header("Access-Control-Allow-Origin", "*")
                    .header("Access-Control-Allow-Credentials", "true")
                    .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
                    .header("Access-Control-Max-Age", "1209600");

            requestContext.abortWith(responseBuilder.build());
        }
    }
}

<强> ApplicationConfig.java

注意:在此添加过滤器,因此不必包含在web.xml中

@ApplicationScoped 
@ApplicationPath("/api")
public class ApplicationConfig extends Application
{
    @Override
    public Set<Class<?>> getClasses()
    {
        return getRestResourceClasses();
    }

    private Set<Class<?>> getRestResourceClasses()
    {
        Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
        resources.add(com.example.AuthorizeFilter.class);
        resources.add(com.example.UserService.class);
        return resources;
    }
}