如何在一个应用程序中部署多个RestEasyInterceptors?

时间:2015-07-11 15:26:02

标签: java jboss web.xml resteasy interceptor

我正在JBoss 7.1.1上使用RestEasy部署一些Web服务。我有两个类,它们沿同一路径处理不同的资源请求。 UserAPI.java处理/books/books/{id}等.MaingeAPI.java处理/books/checkedout

UserAPI需要先前的登录,该登录由名为AuthInterceptor的RestEasyInterceptor验证。维护API不需要登录,但只能通过localhost作为GET访问;它由LocalInterceptor处理。

为了使它们分开,AuthInterceptor的accept()方法如果declaring.getName().contains("Maintenance")则返回false - 所以它根本不应该处理/books/checkedout。如果declaring.equals(MaintenanceAPI.class),则LocalInterceptor的accept()方法返回true。

AuthInterceptor在' resteasy.providers'下的web.xml中声明。但Eclipse认为XML元素中只能有一个参数,因此LocalInterceptor具有注释@Provider,希望它能够神奇地工作。

拒绝XML配置

这失败了:

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>com.foo.AuthInterceptor</param-value>
    <param-value>com.foo.LocalInterceptor</param-value>
</context-param>

我不确定正确的语法是什么,RestEasy文档中没有任何帮助。这也失败了:

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>com.foo.AuthInterceptor</param-value>
</context-param>
<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>com.foo.LocalInterceptor</param-value>
</context-param>

然而,正在发生的事情是,从远程计算机到/books/checkedout哪个应该以NOT_AUTHORIZED 失败)的URL请求被路由到UserAPI&#39; { {1}}处理程序,它继续尝试处理它并返回一个非常大的异常!由于以下几个原因,这是错误的:它不是正确的路径,不是正确的处理程序,不是正确的参数类型,它应该需要一个身份验证令牌!显然,LocalInterceptor并没有通过注释神奇地添加,因此它没有机会接受请求。 AuthInterceptor必须正确拒绝它,但为什么要求UserAPI处理它呢?此外,为什么/books/{id}/会被赋予字符串&#34; checkedout&#34;?

我还没有发布代码,因为我问我是否能做我正在尝试的事情(比如&#34;为什么我可以将String&#34子类化; - 不需要代码如果你理解这个问题)以及为什么映射试图做错事而不是失败。

也许它就像让web.xml正确一样简单?

1 个答案:

答案 0 :(得分:0)

对于您的XML配置(通常是web.xml ),这是您正在寻找的内容。

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>com.foo.AuthInterceptor, com.foo.LocalInterceptor</param-value>
</context-param>

您应该提供以逗号分隔的完全限定的@Provider类名列表。这将确保您LocalInterceptor也开始工作并完成工作。 see Table 2.1 of the Documentation

也就是说,我假设您在@PathMaintenanceAPI接口上的UserAPI注释看起来都像@Path("/books")。我认为这是你问题的根源。

/books/checkedout与接口条目级别的/books/{id}差异不大,直到请求到达处理方法,以便/books/checkedout的请求进入UserAPI首先(期待/books/{id}),字符串"checkedout"将被读作整数{id},因此你的非常大的例外...( i&#39; m在StackTrace的根目录下猜测它是 NumberFormatException 。)

在你的鞋子里,我会只用一个界面来处理所有请求@Path("/books")并改变我的方法路径,以免彼此冲突。

@Path("/books")
@Produces(MediaType.APPLICATION_JSON)
public interface BooksAPI{

    @GET
    @Path("/{id}")
    public Response getBookByID(@PathParam("id") String id);
    // so the url here looks like --> /books/1234

    @GET
    @Path("/status/checkedout")
    public Response getCheckedOut(); 
    // and the url here looks like --> /books/status/checkedout
}

OR 如果我决定保留两个接口,我会使输入路径不同。基本上就是这样的东西;

@Path("/maintenance")
@Produces(MediaType.APPLICATION_JSON)
public interface MaintenanceAPI{

    @GET
    @Path("/books/checkedout")
    public Response getCheckedOut(); 
}
// so the url here looks like --> /maintenance/books/checkedout

@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public interface UserAPI{

    @GET
    @Path("/books/{id}")
    public Response getBookByID(@PathParam("id") String id);
}
// and the url here looks like --> /users/books/1234



其他建议 - 过滤来自localhostremote的请求,我曾经使用的一种非常强大的技术是访问/角色管理,具有类似的功能;

创建一个注释界面,例如

@Documented
@Retention (RUNTIME)
@Target({TYPE, METHOD})
public @interface RolesAllowed {
    String[] value();
}

然后你的拦截器的accept()方法可能如下所示;

<强> LocalInterceptor.java

@Override
public boolean accept(Class declaring, Method method) {
    if(method.isAnnotationPresent(RolesAllowed.class)){
        RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
        Set<String> rolesSet = new HashSet<String (Arrays.asList(rolesAnnotation.value()));
        return rolesSet.contains("local");
    }
    return false;
}

<强> AuthInterceptor.java

@Override
public boolean accept(Class declaring, Method method) {
    if(method.isAnnotationPresent(RolesAllowed.class)){
        RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
        Set<String> rolesSet = new HashSet<String (Arrays.asList(rolesAnnotation.value()));
        return rolesSet.contains("remote");
    }
    return false;
}

最后根据需要使用所需的角色注释您的接口方法。 (注意 - 我使用上面提到的第一种方法)例如

@Path("/books")
@Produces(MediaType.APPLICATION_JSON)
public interface BooksAPI{

    @GET
    @Path("/{id}")
    @RolesAllowed({"remote"})
    public Response getBookByID(@PathParam("id") String id);

    @GET
    @Path("/status/checkedout")
    @RolesAllowed({"local"})
    public Response getCheckedOut(); 
}