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

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

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

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

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

用法:

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

此时我假设一个HttpContext Injectable对一个方法不起作用,但我不知道我可以用什么其他选项来实现这个注释。

如果您不想使用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"); } } } } 

在Dropwizard中,添加如下提供程序:environment.addProvider(CustomDispatchAdapter.class);

编辑这适用于JAX-RS 2.0。 虽然泽西岛现在的版本是2.4.1,但是Dropwizard很遗憾地仍在使用1.17.1 :(。

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

一,注释:

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

接下来,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()); } } } 

最后,注册filter。 这取决于您如何设置Jersey应用程序。 以下是您可能设置的两种方法,但还有许多其他可能性,因此我不会全部介绍它们。

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

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

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

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

用法:

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

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

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

首先创建注释

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

然后配置guice包

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

模块绑定方法拦截器

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

检查连接是否安全(注意:在此示例中,如果连接不安全,则报告资源未被发现,您可能需要针对您的用例进行调整)

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

最后,带有安全方法的资源看起来像

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