Java 使用 Jersey 的 AbstractHttpContextInjectable 的自定义方法注释不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19785001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 20:11:17  来源:igfitidea点击:

Custom Method Annotation using Jersey's AbstractHttpContextInjectable not Working

javaannotationsjerseydropwizard

提问by IAE

I want to restrict some methods if they are being accessed in a non-secure manner. I'm creating a @Secure annotation that checks whether or not the request was sent over secure channels. However, I cannot create a method injectable that captures the HttpContext of the request.

如果以不安全的方式访问某些方法,我想限制它们。我正在创建一个 @Secure 注释,用于检查请求是否通过安全通道发送。但是,我无法创建一个可注入的方法来捕获请求的 HttpContext。

@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'm using the Dropwizard framework, so initialization of the Providers should be as easy as:

我正在使用 Dropwizard 框架,因此提供程序的初始化应该很简单:

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

Usage:

用法:

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

At this point I'm assuming that a HttpContext Injectable doesn't work on a method, but I'm at a loss as to what other options I could utilize to implement this annotation.

在这一点上,我假设 HttpContext Injectable 不适用于某个方法,但我不知道我可以利用哪些其他选项来实现此注释。

采纳答案by John R

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

如果你不想使用 AOP,我认为你可以通过实现 ResourceMethodDispatchProvider 和 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 Dropwizard, add the provider like this: environment.addProvider(CustomDispatchAdapter.class);

在 Dropwizard 中,像这样添加提供者: environment.addProvider(CustomDispatchAdapter.class);

回答by Alden

EDITthis works with JAX-RS 2.0. Though Jersey is now on version 2.4.1, Dropwizard is sadly still using 1.17.1 :(.

编辑这适用于 JAX-RS 2.0。尽管 Jersey 现在是 2.4.1 版本,但遗憾的是 Dropwizard 仍在使用 1.17.1 :(。

You could use a ContainerRequestFiltertogether with your annotation.

您可以将 aContainerRequestFilter与您的注释一起使用。

First, the annotation:

一、注解:

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

Next, the 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 lastly, registering the filter. This depends on how you set up your Jersey app. Here are two ways you might have set it up, but there are many other possibilities so I won't cover them all.

最后,注册过滤器。这取决于您如何设置 Jersey 应用程序。您可以通过以下两种方式进行设置,但还有许多其他可能性,因此我不会全部介绍。

If you have a ResourceConfigwith grizzly, you would want this:

如果你有一个ResourceConfig灰熊,你会想要这个:

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

If you're using the custom application model:

如果您使用自定义应用程序模型:

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

Usage:

用法:

@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();
    }
}

回答by Jonas

Allowing annotated methods to be accessed only via secure channel can be done using AOP. Please find the solution using Guice and it's AOP capabilities (of course other AOP solutions could be used).

可以使用 AOP 来允许仅通过安全通道访问带注释的方法。请使用 Guice 找到解决方案及其 AOP 功能(当然也可以使用其他 AOP 解决方案)。

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

您将需要 Guice 库 (com.google.inject:guice:3.0)。

First of all create annotation

首先创建注解

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

then configure guice bundle

然后配置guice包

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 method interceptor

模块绑定方法拦截器

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 connection is secure (note: in this example resource is reported as not found if connection is not secure, you might need to adjust this 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 the resource with secured 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";
  }

}