阅读(4452) (0)

Shiro 测试

2021-06-19 14:56:49 更新

我们知道 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 方法。这些方法在主题线程关联的参考中介绍。