Authentication DropWizard中的自定义授权问题

Authentication DropWizard中的自定义授权问题,authentication,authorization,dropwizard,Authentication,Authorization,Dropwizard,我正在尝试在dropwizard中添加自定义授权,但无法成功 我通过将dropwizard绑定到authFactory,为其添加了自定义身份验证 Authenticator ssoAuthenticator = createSSOAuthenticator(configuration.getSsoGrantClientConfiguration()); environment.jersey().register(AuthFactory.binder( ne

我正在尝试在dropwizard中添加自定义授权,但无法成功

我通过将dropwizard绑定到authFactory,为其添加了自定义身份验证

Authenticator ssoAuthenticator = createSSOAuthenticator(configuration.getSsoGrantClientConfiguration());
environment.jersey().register(AuthFactory.binder(
                    new SSOTokenAuthFactory<SSOGrant>(
                                       ssoAuthenticator,
                                          SYSTEM_PREFIX,
                                         SSOGrant.class))
 );
下面是创建的注释

@Documented
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER,java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})
public @interface PermissionsAllowed {
    String[] value();
}
我正在检查方法上是否存在注释,然后注册一个过滤器

public class PermissionDynamicFeature implements DynamicFeature {
@Override
    public void configure(final ResourceInfo resourceInfo, final FeatureContext configuration) {

        final AnnotatedMethod am = new AnnotatedMethod(resourceInfo.getResourceMethod());
        final Annotation[][] parameterAnnotations = am.getParameterAnnotations();
            for (Annotation[] annotations : parameterAnnotations) {
                for (Annotation annotation : annotations) {
                    if (annotation instanceof PermissionsAllowed) {
                        configuration.register(new RolesAllowedRequestFilter(((PermissionsAllowed)annotation).value()));
                        return;
                    }
                }
            }
        }



    //@Priority(Priorities.USER) // authorization filter - should go after any authentication filters
    private static class RolesAllowedRequestFilter implements ContainerRequestFilter {

        private final boolean denyAll;
        private final String[] rolesAllowed;

        RolesAllowedRequestFilter() {
            this.denyAll = true;
            this.rolesAllowed = null;
        }

        RolesAllowedRequestFilter(final String[] rolesAllowed) {
            this.denyAll = false;
            this.rolesAllowed = (rolesAllowed != null) ? rolesAllowed : new String[] {};
        }

        @Override
        public void filter(final ContainerRequestContext requestContext) throws IOException {
            if (!denyAll) {
                if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
                    throw new ForbiddenException(LocalizationMessages.USER_NOT_AUTHORIZED());
                }

                for (final String role : rolesAllowed) {
                    if (requestContext.getSecurityContext().isUserInRole(role)) {
                        return;
                    }
                }
            }

            throw new ForbiddenException(LocalizationMessages.USER_NOT_AUTHORIZED());
        }

        private static boolean isAuthenticated(final ContainerRequestContext requestContext) {
            return requestContext.getSecurityContext().getUserPrincipal() != null;
        }
    }
}
我只是试图根据与RoleAllowed filter相同的行来构建我的授权

我面临的问题是在身份验证之前调用授权过滤器。 我缺少什么,以便先进行身份验证,然后调用授权筛选器

注册RoleAllowedDynamicFeature时也会发生同样的情况

environment.jersey().register(RolesAllowedDynamicFeature.class);

RoleSalowedDynamicFeature甚至在进行身份验证之前就被调用了。

因此,根据您的回答,我编写了一个测试,我相信我可以告诉您这里的问题所在

用于提供身份验证令牌的Authfactory不是请求筛选器。根据jersey的文档,这是请求的执行顺序:

问题如下:

在执行绑定之前,将始终执行所有请求筛选器。在请求时,jersey甚至不知道是否需要将任何内容绑定到您的方法。为什么要创建任何东西呢?过滤器可能会在请求开始执行之前拒绝请求

因此,简而言之,用@Auth注释资源方法只需向jersey环境添加一个注入绑定器。您可以在此处阅读有关自定义注入的信息:

这显然是正确的,非常方便,但不是你想要的。 您想要的是在请求通过任何筛选器之前拒绝它。为此,您必须编写一个请求筛选器。用正确的优先级注释它,一切都应该正常工作

您可以考虑将身份验证逻辑提取到一个公共类中(您已经这样做了),然后向RequestFilter和验证器注册同一个类,从而保留身份验证提供程序,同时仍然基于身份验证进行请求过滤

run(...) {

   myAuthStuff = create()
   jersey.register(MyAuthRequstFilter(myAuthStuff));
   jersey.register(MyAuthInjectionBinder(myAuthStuff));

}
添加一个缓存,您就不必担心调用同一事物两次

我相信dropwizard并不打算将其用于过滤器。他们的意图似乎是将auth上下文注入到资源方法中,并在其中进行身份验证等。不是我喜欢的解决方案,但它可以工作

总结一下:

退出向导不支持您要执行的操作。解决方案是将他们的解决方案扩展到请求过滤器中

希望有帮助


Artur

您可以按优先级控制jersey筛选器的执行:已尝试但仍不工作。首先调用筛选器,然后调用身份验证块。筛选器是身份验证块。动态特性在构建RESTAPI时被调用,它们用于在正确执行时注册过滤器。所以这是正常的。您的筛选器没有订单集。您可以查看Priorities类,jersey甚至定义了身份验证和授权的特殊优先级。如果您可以使用一个我可以运行的小示例更新您的问题,我很乐意仔细查看。主要问题是,在调用AuthFactory注册的身份验证之前,调用了通过DynamicFeature添加的权限检查筛选器。我添加了Priority@Priority(Priorities.AUTHORIZATION)并测试了它在身份验证之前仍然被调用。即使我不添加自定义权限检查过滤器,只添加
environment.jersey().register(RolesAllowedDynamicFeature.class)
在应用程序中,然后在实际身份验证发生且失败之前调用RoleAllowed筛选器。我将尝试传递示例。因此,仔细观察AuthFactory,我认为这是您的问题。验证工厂不提供优先级/顺序。您能否尝试将AuthFactory实现为一个具有优先级的单独过滤器,并查看它是否有效?或者,您可以将@Priority(Integer.MAX_INT)添加到其他筛选器中吗?这应该把它们放在用户优先级之后,让它们最后执行。它会指向AuthFactory中的一个bug。因此,我们需要在两个地方添加authenticationon部分,一个是使用AuthFactory,我目前正在做,另一个是将authenticator添加到过滤器中。因此,首先使用过滤器完成身份验证,然后调用PermissionChecked过滤器,@Auth将使用AuthFactory调用身份验证。我的理解正确吗?你提到了缓存,你能详细解释一下你所说的缓存是什么意思吗?是的,你是对的,你必须添加两次。缓存是因为过滤器和AuthFactory本质上会做相同的事情,对吗?因此,通过缓存第一个身份验证调用,您无需再次进行身份验证(身份验证可能会很昂贵)。(以guava加载缓存为例)。您还可以考虑跳过AuthFactory,转而执行过滤器中的所有逻辑。您真的需要方法中的Auth对象吗?如果是这样的话,您可以随时将其存储在筛选器中的某个位置(例如threadLocal)并以这种方式重复使用。感谢您的快速响应和帮助。谢谢。所以在过滤器中,我创建SSOTokeAuthFactory的实例并调用其提供方法,它应该可以正常工作,对吗?这取决于您的实现。如果您的工厂需要注射,您可能需要手动注射。提取该逻辑并直接用当前请求调用它可能更好。看看BasicAuthFactory,它将不得不注入请求。如果您想使用该代码,您还必须担心线程安全。基本上,我会提取Provide中的逻辑,有一个线程安全的助手类,它有一个方法Provide(httpRequest),然后在我的工厂和过滤器中用请求调用该方法。
run(...) {

   myAuthStuff = create()
   jersey.register(MyAuthRequstFilter(myAuthStuff));
   jersey.register(MyAuthInjectionBinder(myAuthStuff));

}