Shiro 测试
我们知道 Subject 是“当前执行中”用户的特定于安全性的视图,并且 Subject 实例始终绑定到线程,以确保我们知道谁在任何时候都在执行逻辑线程执行期间的时间。
这意味着必须始终发生三件事,以支持能够访问当前正在执行的 Subject:
- 必须创建一个
Subject
实例 -
Subject
实例必须“绑定”到当前正在执行的线程。 - 线程完成执行之后(或者如果线程的执行结果为
Throwable
),则Subject
必须为“未绑定” *,以确保线程在任何线程池化的环境中保持“干净”。
Shiro 的体系结构组件可为正在运行的应用程序自动执行此绑定/取消绑定逻辑。例如,在 Web 应用程序中,根 Shiro 过滤器在过滤请求时执行此逻辑。但是,由于测试环境和框架不同,我们需要针对所选测试框架自己执行此绑定/解除绑定逻辑。
测试设置
因此,我们知道在创建Subject
实例后,必须将其绑定到线程。在线程(或本例中为测试)完成执行之后,我们必须* unbind * Subject 以保持线程“干净”。
幸运的是,现代测试框架(如 JUnit 和 TestNG)本身已经支持“设置”和“拆卸”这一概念。我们可以利用这种支持来模拟 Shiro 在“完整”应用程序中将执行的操作。我们已经创建了一个基础抽象类,您可以在下面的自己的测试中使用它-可以随意复制和/或修改。它可以用于单元测试和集成测试(在此示例中,我们使用的是 JUnit,但是 TestNG 也可以工作):
因此,我们知道在创建Subject实例后,必须将其绑定到线程。在线程(或本例中为测试)完成执行之后,我们必须* unbind * Subject 以保持线程“干净”。
幸运的是,现代测试框架(如 JUnit 和 TestNG)本身已经支持“设置”和“拆卸”这一概念。我们可以利用这种支持来模拟 Shiro 在“完整”应用程序中将执行的操作。我们已经创建了一个基础抽象类,您可以在下面的自己的测试中使用它-可以随意复制和/或修改。它可以用于单元测试和集成测试(在此示例中,我们使用的是 JUnit,但是 TestNG 也可以工作):
Shiro测试摘要
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);
}
}
测试和框架
AbstractShiroTest
类中的代码使用 Shiro 的ThreadState
概念和静态 SecurityManager。这些技术在测试和框架代码中很有用,但很少在应用程序代码中使用。
大多数需要确保线程状态一致性的与 Shiro 合作的最终用户,几乎都会使用 Shiro 的自动 Management 机制,即 Subject.associateWith 和 Subject.execute 方法。这些方法在主题线程关联的参考中介绍。