Shiro的subject實質上是目前執行使用者的一個特定的視圖,subject能夠被通路的前提條件:
1.subject的執行個體必須被建立;
2.subject執行個體必須和目前執行的線程綁定;
3.subject執行個體線上程結束後必須解除綁定,以保證線程可以被線程池清理。
AbstractShiroTest
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.LifecycleUtils;
import org.apache.shiro.util.ThreadState;
import org.junit.AfterClass;
/**
* Abstract test case enabling Shiro in test environments.
*/
public abstract class AbstractShiroTest {
private static ThreadState subjectThreadState;
public AbstractShiroTest() {
}
/**
* Allows subclasses to set the currently executing {@link Subject} instance.
*
* @param subject the Subject instance
*/
protected void setSubject(Subject subject) {
clearSubject();
subjectThreadState = createThreadState(subject);
subjectThreadState.bind();
}
protected Subject getSubject() {
return SecurityUtils.getSubject();
}
protected ThreadState createThreadState(Subject subject) {
return new SubjectThreadState(subject);
}
/**
* Clears Shiro's thread state, ensuring the thread remains clean for future test execution.
*/
protected void clearSubject() {
doClearSubject();
}
private static void doClearSubject() {
if (subjectThreadState != null) {
subjectThreadState.clear();
subjectThreadState = null;
}
}
protected static void setSecurityManager(SecurityManager securityManager) {
SecurityUtils.setSecurityManager(securityManager);
}
protected static SecurityManager getSecurityManager() {
return SecurityUtils.getSecurityManager();
}
@AfterClass
public static void tearDownShiro() {
doClearSubject();
try {
SecurityManager securityManager = getSecurityManager();
LifecycleUtils.destroy(securityManager);
} catch (UnavailableSecurityManagerException e) {
//we don't care about this when cleaning up the test environment
//(for example, maybe the subclass is a unit test and it didn't
// need a SecurityManager instance because it was using only
// mock Subject instances)
}
setSecurityManager(null);
}
}
ExampleShiroUnitTest
import org.apache.shiro.subject.Subject;
import org.junit.After;
import org.junit.Test;
import static org.easymock.EasyMock.*;
/**
* Simple example test class showing how one may perform unit tests for code that requires Shiro APIs.
*/
public class ExampleShiroUnitTest extends AbstractShiroTest {
@Test
public void testSimple() {
//1. Create a mock authenticated Subject instance for the test to run:
Subject subjectUnderTest = createNiceMock(Subject.class);
expect(subjectUnderTest.isAuthenticated()).andReturn(true);
//2. Bind the subject to the current thread:
setSubject(subjectUnderTest);
//perform test logic here. Any call to
//SecurityUtils.getSubject() directly (or nested in the
//call stack) will work properly.
}
@After
public void tearDownSubject() {
//3. Unbind the subject from the current thread:
clearSubject();
}
}
Unit下內建測試(ExampleShiroIntegrationTest)
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
public class ExampleShiroIntegrationTest extends AbstractShiroTest {
@BeforeClass
public static void beforeClass() {
//0. Build and set the SecurityManager used to build Subject instances used in your tests
// This typically only needs to be done once per class if your shiro.ini doesn't change,
// otherwise, you'll need to do this logic in each test that is different
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:test.shiro.ini");
setSecurityManager(factory.getInstance());
}
@Test
public void testSimple() {
//1. Build the Subject instance for the test to run:
Subject subjectUnderTest = new Subject.Builder(getSecurityManager()).buildSubject();
//2. Bind the subject to the current thread:
setSubject(subjectUnderTest);
//perform test logic here. Any call to
//SecurityUtils.getSubject() directly (or nested in the
//call stack) will work properly.
}
@AfterClass
public void tearDownSubject() {
//3. Unbind the subject from the current thread:
clearSubject();
}
}