Jersey 2 - ContainerRequestFilter get method annot

2019-08-02 16:30发布

Im trying to get the Method annotation in ContainerRequestFilter object.

Controler:

@GET
@RolesAllowed("ADMIN")
public String message() {
    return "Hello, rest12!";
}

ContainerRequestFilter :

@Provider
public class SecurityInterceptor implements  javax.ws.rs.container.ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
//Here I need To get the @RolesAllowed("ADMIN") annotation value
}

Application :

@ApplicationPath("/rest")
public class ExpertApp extends Application {
private final HashSet<Object> singletons = new LinkedHashSet<Object>();

public ExpertApp() {
    singletons.add(new SecurityInterceptor());
}   

@Override
public Set<Object> getSingletons() {
    return singletons;
}

public Set<Class<?>> getClasses() {
    return new HashSet<Class<?>>(Arrays.asList(UserControler.class, SearchController.class));

}

}

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<!-- Servlet declaration can be omitted in which case it would be automatically 
    added by Jersey -->
<servlet>
    <servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>

<servlet-mapping>
    <servlet-name>javax.ws.rs.core.Application</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

How do I ge the @RolesAllowed("ADMIN") value,

2条回答
放我归山
2楼-- · 2019-08-02 16:54

Your ContainerRequestFilter is implemented as post-matching filters. It means that the filters would be applied only after a suitable resource method has been selected to process the actual request i.e. after request matching happens.

So, @RolesAllowed("ADMIN") will block the call and your filter will never be called.

To avoid that issue, I create custom annotation; for instance:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyRoles {
    public enum MyRole {
        USER, OFFICER, COMPLIANCE, ADMIN
    }

    MyRole role() default MyRole.USER;

    Class<? extends Throwable> expected() default None.class;

    static class None extends Throwable {

        /**
             * 
             */
        private static final long serialVersionUID = 1L;
    }
}

In my webservice, I can annotate the method:

@POST
@MyRoles
@Path("/secured")
@Produces("application/json")
@Consumes("application/json")
public String mySecuredMethod() {
    // This method is annotated with @MyRoles
    // The authentication filter will be executed before invoking this
    // method
    return "{message='secured'}";
}

And in the filter, I check for the custom annotation:

private static final String AUTHORIZATION_PROPERTY = "Authorization";
private static final String AUTHENTICATION_SCHEME = "Basic";
private static final ServerResponse BAD_REQUEST = new ServerResponse("Token invalid or expired", 400, new Headers<Object>());;
private static final ServerResponse ACCESS_DENIED = new ServerResponse("Access denied for this resource", 401, new Headers<Object>());;
private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("Nobody can access this resource", 403, new Headers<Object>());;
private static final ServerResponse SERVER_ERROR = new ServerResponse("INTERNAL SERVER ERROR", 500, new Headers<Object>());;

@Override
public void filter(ContainerRequestContext requestContext) {
    System.err.println("GFA Debug SecurityInterceptor ............ ");
    System.err.println(requestContext.getUriInfo().getRequestUri());

    Method method = resourceInfo.getResourceMethod();
    System.out.println("GFA DEbug method.getName() " + method.getName());

    System.out.println("GFA DEbug method.isAnnotationPresent(PermitAll.class) = " + method.isAnnotationPresent(PermitAll.class));

    // Access denied for all
    if (method.isAnnotationPresent(DenyAll.class)) {
        requestContext.abortWith(ACCESS_FORBIDDEN);
        return;
    }

    // Access allowed for all
    if (method.isAnnotationPresent(PermitAll.class)) {
        System.out.println("GFA debug permitAll ... bye");
        return;
    }

    // Custom roles
    System.out.println("GFA Debug method.isAnnotationPresent(MyRole.class) = " + method.isAnnotationPresent(MyRoles.class));
    if (!method.isAnnotationPresent(MyRoles.class)) {
        requestContext.abortWith(ACCESS_FORBIDDEN);
        return;
    }

    MyRoles myannotation = method.getAnnotation(MyRoles.class);
    System.out.println("GFA custom role ... " + myannotation.role());

    // Then I check for token and validity of role, etc.
}
查看更多
做自己的国王
3楼-- · 2019-08-02 17:02

You could...

Inject into your filter @Context ResourceInfo, as seen here, and get the annotation from the Method

RolesAllowed annot = resourceInfo.getResourceMethod().getAnnotation(RolesAllowed.class);

But...

Jersey already has a RolesAllowedDynamicFeature that implements the access control for the annotations @RolesAllowed, @PermitAll and @DenyAll. You just need to register the feature with your application

In ResourceConfig

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        super(MyResource.class);
        register(RolesAllowedDynamicFeature.class);
    }
}

In web.xml

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>
        org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature
    </param-value>
</init-param>

Or in your Application subclass, you can add it to your getSingletons() or getClasses() set. Doesn't make much difference which one. No injections occur, so it would be safe to just instantiate it and add it to the singletons.

Note: The first option can be done in any JAX-RS 2.0 application, while the second is Jersey specific.

查看更多
登录 后发表回答