源碼注釋
根據運作時環境,為調用代碼通路目前可通路的Subject。
由上可見,SecurityUtils主要是為了擷取Subject的,然後通過Subject進行一系列的驗證動作。
API
- private static volatile SecurityManager securityManager;
提供了一個靜态的私有變量SecurityManager,相當于一個程式的SecurityManager模闆,在目前線程記憶體儲的SecurityManager為空時,就傳回此變量。
- public static Subject getSubject(){…}
整個SecurityUtils的核心方法,從ThreadContext(基于ThreadLocal的存儲Subject和SecurityManager)擷取Subject。目前線程沒有Subject的話就建立一個Subject并放進ThreadContext中。
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
- public static void setSecurityManager(SecurityManager securityManager) {…}
設定SecurityUtils.securityManager。
public static void setSecurityManager(SecurityManager securityManager) {
SecurityUtils.securityManager = securityManager;
}
- public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {…}
先從ThreadContext(目前線程中)中擷取SecurityManager,如果不存在,就傳回SecurityUtils.securityManager。
public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
SecurityManager securityManager = ThreadContext.getSecurityManager();
if (securityManager == null) {
securityManager = SecurityUtils.securityManager;
}
if (securityManager == null) {
String msg = "No SecurityManager accessible to the calling code, either bound to the " +
ThreadContext.class.getName() + " or as a vm static singleton. This is an invalid application " +
"configuration.";
throw new UnavailableSecurityManagerException(msg);
}
return securityManager;
}
問
1、為何getSecurityManager()方法中當ThreadContext的SecurityManager 為空時,傳回SecurityUtils.securityManager,但不将SecurityUtils.securityManager存進目前線程的ThreadContext中呢?那ThreadContext的SecurityManager什麼時候放進去的呢?
擴充
1、getSubject中的(new Subject.Builder()).buildSubject()解析:
Builder為Subject的靜态内部類,其内部有兩個私有final變量subjectContext和securityManager。subjectContext為DefaultSubjectContext,預設的securityManager為SecurityUtils.securityManager。
内部方法支援設定subjectContext的各個屬性值
public Builder sessionId(Serializable sessionId);
設定subjectContext的SessionId。確定正在建構的Subject将使用指定的sessionId執行個體。
public Builder session(Session session);
設定subjectContext的Session。確定正在建構的Subject将使用指定的Session執行個體。但是使用sessionId建構器方法比為該方法構造執行個體更常見。
public Builder host(String host);
設定subjectContext的host。確定正在建構的Subject将反映指定的主機名或IP作為其起源的位置。
public Builder principals(PrincipalCollection principals);
設定subjectContext的PrincipalCollection 。確定正在建構的Subject将反映指定的主體(又名辨別)。即建構此辨別的Subject。例:
PrincipalCollection identity = new org.apache.shiro.subject.SimplePrincipalCollection(Object, String); Subject jsmith = new Subject.Builder().principals(identity).buildSubject();
public Builder sessionCreationEnabled(boolean enabled);
設定subjectContext的SessionCreationEnabled。配置建立的Subject執行個體的時候是否可以建立一個新的Session (如果不存在的話)。如果設定為false,任何對subject.getSession()或subject.getSession(true))的應用程式調用都會導緻SessionException。預設是true。
public Builder authenticated(boolean authenticated);
設定目前建立的Subject執行個體是否應視為已驗證,如果為true,則org.apache.shiro.subject.Subject#isAuthenticated()傳回true。
public Builder contextAttribute(String attributeKey, Object attributeValue);
将自定義屬性添加到底層上下文MapContext中,用于構造Subject執行個體。
public Subject buildSubject();
建立一個Subject,調用的是目前Builder中SecurityManager的createSubject方法。
後續代碼将在SecurityManager中進行分析。
this.securityManager.createSubject(this.subjectContext)