Shiro can be configured dynamically in a single JVM - I do it all the time
on a product I'm working on. I guess it means what you mean by 'configure
dynamically'. I'm not swapping out the security manager entirely however.
I re-configure the same instance (maybe swap out its AuthorizationStrategy,
etc).
When a Subject instance is created (a DelegatingSubject specifically), it
accepts a SecurityManager instance at that time. It holds on to that
instance throughout it's lifetime. In other words, you can call
SecurityUtils.setSecurityManager at any time after that point (which sets a
VM-static singleton) and the previously instantiated Subject instance(s)
will not 'see' the new static SecurityManager instance.
Subject object instances themselves are intended to have a very short VM
lifetime - typically instantiated at the beginning of a thread's execution
and removed/garbage collected at the end of its execution. Swapping out a
SecurityManager entirely during a thread's execution is not something that
people have requested in the past, so we haven't built it. I'm not sure how
often this would occur in practice for running applications to be honest.
So to sidestep this for unit testing scenarios, there are two good
approaches that I can think of:
1) Use a Subject.Builder and pass in the SecurityManager instance you want
it to use during the test. The constructed instance is used to 'execute' a
method under test:
Subject subject = new Subject.Builder(mySecurityManager).buildSubject();
subject.execute( new Runnable() {
executeMethodUnderTest();
});
When 'executeMethodUnderTest' executes, the subject.execute method will
guarantee thread set-up and clean-up before and after the method's
execution, respectively. Also, any call to SecurityUtils.getSubject()
within that method (or any methods it calls) will work correctly.
2) Set up and tear down the Shiro thread state before and after a test
methods's execution. This is the exact same thing as #1, but requires you
to do a little more work. See the http://shiro.apache.org/subject.html page,
"Manual Association" section for more detail. You would do this in @Before
and @After methods in JUnit:
private ThreadState shiroThreadState;
@Before
public void setUpSubject() {
Subject testSubject = Subject.Builder(securityManagerForTheTest). ...
.buildSubject();
shiroThreadState = new SubjectThreadState(testSubject);
shiroThreadState.bind();
}
@After
public void tearDownSubject() {
shiroThreadState.restore(); //or clear(); either is fine.
}
#1 is easier, but it might make your test cases look a little ugly. #2 is a
little more time consuming, but more flexible and arguably nicer when
keeping your tests clean. You can have this in a test superclass and your
subclasses wouldn't ever have to worry about it. They could call
SecurityUtils.getSubject() at any time and it would all work out.
Note that the Subject.Builder constructor doesn't require a securityManager
instance - if you don't provide one, it will call
SecurityUtils.getSecurityManager() and use that one instead. I typically
like to specify the instance myself because I personally don't like using
static singletons if I can avoid them, and at least in testing, I find it to
be a bit more deterministic.
Anyway, I hope this helps. Does this all make sense?
Best,
--
Les Hazlewood
Founder, Katasoft, Inc.
Application Security Products & Professional Apache Shiro Support and
Training:
http://www.katasoft.com
On Tue, Nov 9, 2010 at 12:47 AM, Janne Jalkanen <[email protected]>wrote:
>
> And to respond to myself - the problem turns out to be that I had one
> instance where Shiro was configured implicitly and not using the
> SHIRO_CONFIG below. Turns out that this causes a conflict if you configure
> Shiro twice with different settings within the same JVM.
>
> It would be good if there was some sort of a check for this - the exception
> message is not exactly helping here.
>
> /Janne
>
> On 8 Nov 2010, at 22:20, Janne Jalkanen wrote:
>
> Folks,
>
> I'm rather stymied at the following problem: Whenever I run my unit tests
> one at a time, everything works. But when I run the entire suite, the tests
> fail due to
>
> SecurityUtils.*getSubject*().logout(); // Ensure we are logged out
> SecurityUtils.*getSubject*().login( *new* UsernamePasswordToken("
> [email protected]","foobar") );
>
> The failure is
>
> org.apache.shiro.authc.IncorrectCredentialsException: The credentials
> provided for account [org.apache.shiro.authc.UsernamePasswordToken -
> [email protected], rememberMe=false] did not match the expected
> credentials.
> at
> org.apache.shiro.realm.AuthenticatingRealm.getAuthenticationInfo(AuthenticatingRealm.java:191)
> at
> org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:179)
> at
> org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:264)
> at
> org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
> at
> org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106)
> at
> org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:269)
> at
> org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:247)
> at
> com.thinglink.site.stripes.AccountActionBeanTest.setup(AccountActionBeanTest.java:43)
> <rest is snipped away>
>
> I've tried different kinds of Shiro configurations, and the one that seems
> to work best right now looks like
>
> /** Configuration that we use in tests. */
> *private* *static* *final* String *SHIRO_CONFIG* =
> "[main]\n"+
> "credentialsMatcher =
> org.apache.shiro.authc.credential.Sha256CredentialsMatcher\n"+
> "credentialsMatcher.storedCredentialsHexEncoded = false\n"+
> "credentialsMatcher.hashSalted = true\n"+
> "cacheManager =
> org.apache.shiro.cache.MemoryConstrainedCacheManager\n"+
> "myRealm = com.thinglink.site.shiro.CassandraRealm\n"+
> "myRealm.credentialsMatcher = $credentialsMatcher\n"+
> "myRealm.cacheManager = $cacheManager\n"+
> "securityManager = org.apache.shiro.mgt.DefaultSecurityManager\n"+
> "securityManager.realm = $myRealm\n";
>
> // For shiro we use our internal configuration, not the one that's found
> from classpath
> Ini ini = *new* Ini();
> ini.load( *SHIRO_CONFIG* );
> IniSecurityManagerFactory smf = *new* IniSecurityManagerFactory(
> ini );
> SecurityUtils.*setSecurityManager*( smf.getInstance() );
>
> I've tried using a WebIniSecurityManagerFactory, but the test framework I'm
> using does not fully implement the Servlet spec, so it's not that useful; it
> fails in interesting ways and getting cookies is a pain. So I figured I
> should try just simply the basic DefaultSecurityManager as everything runs
> in a single thread anyway.
>
> This is with 1.1.0 release.
>
> Any ideas as to what could explain why the tests aren't running if I run
> all the tests? The fun thing is that this is the *only* test currently which
> actively tries to log in.
>
> /Janne
>
>