挂毯替代身份验证器

问题描述

我正在尝试为挂毯安全性( org.tynamo.security )使用自定义身份验证器。

我有一个自定义的身份验证器

public class EnvironmentalRealmAuthenticator extends ModularRealmAuthenticator

在我的模块中,我覆盖了Tapestry(ModularRealmAuthenticator)的认身份验证器:

public static void bind(final ServiceBinder binder) {

    binder.bind(Authenticator.class,EnvironmentalRealmAuthenticator.class).withId("EnvironmentalRealmAuthenticator");
}

@Contribute(ServiceOverride.class)
public static void setupOverrides(final MappedConfiguration<Class,Object> configuration,@Local final Authenticator override) {
    configuration.add(Authenticator.class,override);
}

但是,这导致注销时无法清除缓存-我怀疑这是Shiro的DefaultSecurityManager检测身份验证器是否侦听注销的方式造成的:

Authenticator authc = getAuthenticator();
if (authc instanceof logoutAware) {
    ((logoutAware) authc).onlogout(principals);
}

由于EnvironmentalRealmAuthenticator被绑定为Tapestry服务,它最初是作为代理注入的,因此authc instanceof logoutAware会产生false,这就是ModularRealmAuthenticator被绑定的原因。 Tynamo的SecurityModule中的另一种方式:

// TYNAMO-155 It's not enough to identify ModularRealmAuthenticator by it's Authenticator interface only
// because Shiro tests if the object is an instanceof logoutAware to call logout handlers
binder.bind(ModularRealmAuthenticator.class);

但是,当我尝试以这种方式覆盖EnvironmentalRealmAuthenticator

binder.bind(EnvironmentalRealmAuthenticator.class).withId("EnvironmentalRealmAuthenticator");

这会导致以下异常:

由以下原因引起:java.lang.IllegalStateException:由于递归,服务“ ServiceOverride”的构建失败:该服务在某种程度上依赖于自身。请通过org.apache.tapestry5.ioc.modules.TapestryIOCModule.bind(ServiceBinder)(在TapestryIOCModule.java:52中)检查org.apache.tapestry5.ioc.internal.services.ServiceOverrideImpl(Map)(位于ServiceOverrideImpl.java:31) )以引用本身依赖于服务'ServiceOverride'的另一个服务。

解决方法

我不确定是否会看到导致该异常的setupOverrides方法的最终版本。

但是,您是否尝试过以下方法:

public static void bind(final ServiceBinder binder) {

    binder.bind(EnvironmentalRealmAuthenticator.class);
}

@Contribute(ServiceOverride.class)
public static void setupOverrides(final MappedConfiguration<Class,Object> configuration,EnvironmentalRealmAuthenticator override) {
    configuration.add(Authenticator.class,override);
}
,

我似乎找到了一种(相当hacky)的方式。我没有覆盖Authenticator本身,而是覆盖了WebSecuritymanager

public static void bind(final ServiceBinder binder) {
    binder.bind(EnvironmentalRealmAuthenticator.class).withId("EnvironmentalRealmAuthenticator");
    binder.bind(WebSecurityManager.class,EnvironmentalSecurityManager.class).withId("EnvironmentalSecurityManager");
}

@Contribute(ServiceOverride.class)
public static void setupOverrides(final MappedConfiguration<Class,@Local final WebSecurityManager override) {
    configuration.add(WebSecurityManager.class,override);
}

这样,我不必将EnvironmentalRealmAuthenticator与其接口绑定。为了能够识别新的Authenticator,我对模块进行了注释:

@Marker(Primary.class)

EnvironmentalSecurityManager的实现如下所示:

/**
 * Used to properly (and uniquely) identify the authenticator (without having to override it)
 */
public class EnvironmentalSecurityManager extends TapestryRealmSecurityManager {

    private final Logger logger = LoggerFactory.getLogger(EnvironmentalSecurityManager.class);

    /**
     * Mind the @Primary annotation,used to identify the EnvironmentalRealmAuthenticator
     */
    public EnvironmentalSecurityManager(final @Primary Authenticator authenticator,final SubjectFactory subjectFactory,final RememberMeManager rememberMeManager,final Collection<Realm> realms) {

        super(authenticator,subjectFactory,rememberMeManager,realms);
        logger.debug("Created EnvironmentalSecurityManager - class of authenticator is {}",authenticator.getClass());
    }
}

这样,我可以保证使用正确的Authenticator而不必实际覆盖它。