Custom Annotation Method Using Jersey AbstractHttpContextInjectable Doesn't Work

I want to limit some methods if they are accessed in an insecure manner. I am creating an @Secure annotation that checks if a request has been sent over secure channels. However, I cannot create a method that can be used for injection that captures the HttpContext of the request.

@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Secure { } public class SecureProvider<T> implements InjectableProvider<Secure, AbstractResourceMethod> { @Override public ComponentScope getScope() { return ComponentScope.PerRequest; } @Override public Injectable<?> getInjectable(ComponentContext componentContext, Secure annotation, AbstractResourceMethod method) { return new SecureInjectable(); } } public class SecureInjectable<T> extends AbstractHttpContextInjectable<T> { @Override public T getValue(HttpContext context) { // validation here return null; } } 

I use the Dropwizard framework, so initializing the Providers should be as simple as:

 environment.addProvider(new SessionRestrictedToProvider<>(new SessionAuthenticator(), "MySession")); environment.addProvider(new SecureProvider<>()); environment.setSessionHandler(new SessionHandler()); 

Using:

 @Resource @Path("/account") public class AccountResource { @GET @Path("/test_secure") @Secure public Response isSecure() { return Response.ok().build(); } } 

At this point, I assume that the HttpContext Injectable is not working on the method, but I do not understand what other options I could use to implement this annotation.

+7
java jersey annotations dropwizard
source share
3 answers

If you do not want to use AOP, I think you can do this by implementing ResourceMethodDispatchProvider and ResourceMethodDispatchAdapter.

 public class CustomDispatchProvider implements ResourceMethodDispatchProvider { ResourceMethodDispatchProvider provider; CustomDispatchProvider(ResourceMethodDispatchProvider provider) { this.provider = provider; } @Override public RequestDispatcher create(AbstractResourceMethod abstractResourceMethod) { System.out.println("creating new dispatcher for " + abstractResourceMethod); RequestDispatcher defaultDispatcher = provider.create(abstractResourceMethod); if (abstractResourceMethod.getMethod().isAnnotationPresent(Secure.class)) return new DispatcherDecorator(defaultDispatcher); else return defaultDispatcher; } @Provider public static class CustomDispatchAdapter implements ResourceMethodDispatchAdapter { @Override public ResourceMethodDispatchProvider adapt(ResourceMethodDispatchProvider provider) { return new CustomDispatchProvider(provider); } } public static class DispatcherDecorator implements RequestDispatcher { private RequestDispatcher dispatcher; DispatcherDecorator(RequestDispatcher dispatcher) { this.dispatcher = dispatcher; } public void dispatch(Object resource, HttpContext context) { if (context.getRequest().isSecure()) { System.out.println("secure request detected"); this.dispatcher.dispatch(resource, context); } else { System.out.println("request is NOT secure"); throw new RuntimeException("cannot access this resource over an insecure connection"); } } } } 

In the Dropwizard, add the provider as follows: environment.addProvider (CustomDispatchAdapter.class);

+5
source

EDIT works with JAX-RS 2.0. Although Jersey is now on version 2.4.1, Dropwizard, unfortunately, still uses 1.17.1: (.

You can use ContainerRequestFilter along with annotation.

First, the annotation:

 // need a name binding annotation @NameBinding @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Secure { } 

Then filter:

 // filter will only be run for methods that have @Secure annotation @Secure public class SecureFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { // check if HTTPS if (!requestContext.getSecurityContext().isSecure()) { // if not, abort the request requestContext.abortWith(Response.status(Response.Status.BAD_REQUEST) .entity("HTTPS is required.") .build()); } } } 

And finally, filter registration. It depends on how you set up the app in Jersey. Here are two ways you could configure, but there are many other possibilities, so I will not cover them.

If you have a ResourceConfig with grizzly, you need the following:

 final ResourceConfig rc = new ResourceConfig() .packages("my.package.for.resources") .register(SecureFilter.class); 

If you are using a custom application model:

 public class MyApplication extends ResourceConfig { public MyApplication() { packages("my.package.for.resources"); register(SecureFilter.class); } } 

Using:

 @Resource @Path("/account") public class AccountResource { // filter will run for this method @GET @Path("/test_secure") @Secure public Response isSecure() { return Response.ok().build(); } // filter will NOT run for this method @GET @Path("/test_insecure") public Response allowInsecure() { return Response.ok().build(); } } 
+8
source

Allow access to annotated methods only through a secure channel using AOP. Please find a solution using Guice and its AOP features (of course, you can use other AOP solutions).

You will need the Guice library (com.google.inject: guice: 3.0).

First create an annotation

 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Secure {} 

then set up a set of images

 public class SecurableMethodsService extends Service<Configuration> { @Override public void initialize(Bootstrap<Configuration> bootstrap) { bootstrap.addBundle(GuiceBundle.newBuilder().addModule(new SecurableMethodsDemonstrationModule()).build()); } @Override public void run(Configuration configuration, Environment environment) throws Exception { } } 

module binds interceptor method

 public class SecurableMethodsDemonstrationModule extends AbstractModule { @Override protected void configure() { bind(SecuredMethodsContainingResource.class); bindInterceptor(Matchers.any(), Matchers.annotatedWith(Secure.class), new OnlySecureAllowedInterceptor(getProvider(SecurityContext.class))); } } 

which checks if the connection is secure (note: in this example, the resource is reported as not found, if the connection is unsafe, you may need to configure it for your use case)

 public class OnlySecureAllowedInterceptor implements MethodInterceptor { private final Provider<SecurityContext> securityContextProvider; public OnlySecureAllowedInterceptor(Provider<SecurityContext> securityContextProvider) { this.securityContextProvider = securityContextProvider; } public Object invoke(MethodInvocation invocation) throws Throwable { if (!securityContextProvider.get().isSecure()) { throw new NotFoundException(); } return invocation.proceed(); } } 

and finally, a resource with a protected method looks like

 @Path("") public class SecuredMethodsContainingResource { @GET @Path("for-all") public String forAll() { return "for-all"; } @GET @Path("secure") @Secure public String secure() { return "secure"; } } 
+3
source

All Articles