如果以非安全方式访问某些方法,我想限制它们。我正在创建一个@Secure注释,用于检查请求是否是通过安全通道发送的。但是,我无法创建捕获请求的HttpContext的方法injectable。
@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;
}
}
我正在使用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对一个方法不起作用,但我不知道我可以用什么其他选项来实现这个注释。
答案 0 :(得分:8)
编辑这适用于JAX-RS 2.0。虽然Jersey现在使用的是版本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 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());
}
}
}
最后,注册过滤器。这取决于您如何设置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();
}
}
答案 1 :(得分:6)
如果您不想使用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);
答案 2 :(得分:3)
允许仅通过安全通道访问带注释的方法可以使用AOP完成。请使用Guice找到解决方案及其AOP功能(当然可以使用其他AOP解决方案)。
您需要Guice库(com.google.inject:guice:3.0)。
首先创建注释
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Secure {}
然后配置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 {
}
}
模块绑定方法拦截器
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<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();
}
}
最后,带有安全方法的资源看起来像
@Path("")
public class SecuredMethodsContainingResource {
@GET
@Path("for-all")
public String forAll() {
return "for-all";
}
@GET
@Path("secure")
@Secure
public String secure() {
return "secure";
}
}