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