我們知道 Subject 是“當前執(zhí)行中”用戶的特定于安全性的視圖,并且 Subject 實例始終綁定到線程,以確保我們知道誰在任何時候都在執(zhí)行邏輯線程執(zhí)行期間的時間。
這意味著必須始終發(fā)生三件事,以支持能夠訪問當前正在執(zhí)行的 Subject:
Subject
?實例Subject
?實例必須“綁定”到當前正在執(zhí)行的線程。Throwable
?),則?Subject
?必須為“未綁定” *,以確保線程在任何線程池化的環(huán)境中保持“干凈”。Shiro 的體系結(jié)構(gòu)組件可為正在運行的應(yīng)用程序自動執(zhí)行此綁定/取消綁定邏輯。例如,在 Web 應(yīng)用程序中,根 Shiro 過濾器在過濾請求時執(zhí)行此邏輯。但是,由于測試環(huán)境和框架不同,我們需要針對所選測試框架自己執(zhí)行此綁定/解除綁定邏輯。
因此,我們知道在創(chuàng)建?Subject
?實例后,必須將其綁定到線程。在線程(或本例中為測試)完成執(zhí)行之后,我們必須* unbind * Subject 以保持線程“干凈”。
幸運的是,現(xiàn)代測試框架(如 JUnit 和 TestNG)本身已經(jīng)支持“設(shè)置”和“拆卸”這一概念。我們可以利用這種支持來模擬 Shiro 在“完整”應(yīng)用程序中將執(zhí)行的操作。我們已經(jīng)創(chuàng)建了一個基礎(chǔ)抽象類,您可以在下面的自己的測試中使用它-可以隨意復制和/或修改。它可以用于單元測試和集成測試(在此示例中,我們使用的是 JUnit,但是 TestNG 也可以工作):
因此,我們知道在創(chuàng)建Subject實例后,必須將其綁定到線程。在線程(或本例中為測試)完成執(zhí)行之后,我們必須* unbind * Subject 以保持線程“干凈”。
幸運的是,現(xiàn)代測試框架(如 JUnit 和 TestNG)本身已經(jīng)支持“設(shè)置”和“拆卸”這一概念。我們可以利用這種支持來模擬 Shiro 在“完整”應(yīng)用程序中將執(zhí)行的操作。我們已經(jīng)創(chuàng)建了一個基礎(chǔ)抽象類,您可以在下面的自己的測試中使用它-可以隨意復制和/或修改。它可以用于單元測試和集成測試(在此示例中,我們使用的是 JUnit,但是 TestNG 也可以工作):
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
?概念和靜態(tài) SecurityManager。這些技術(shù)在測試和框架代碼中很有用,但很少在應(yīng)用程序代碼中使用。
大多數(shù)需要確保線程狀態(tài)一致性的與 Shiro 合作的最終用戶,幾乎都會使用 Shiro 的自動 Management 機制,即 Subject.associateWith 和 Subject.execute 方法。這些方法在主題線程關(guān)聯(lián)的參考中介紹。
更多建議: