Shiro 中的 SecurityUtils(轉)


在 Shiro 中 SecurityUtils 是一個抽象類。並且沒有任何子類。在其中聲明了一個靜態屬性,三個靜態方法。

靜態屬性 securityManager

private static SecurityManager securityManager; 

用來存儲當前應用中全局唯一的一個SecurityManager。

有兩個靜態方法是為此靜態屬性服務器,也就是下面這兩個:

public static void setSecurityManager(SecurityManager securityManager) { SecurityUtils.securityManager = 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; } 

getSubject 靜態方法

這個是 Shiro 中最核心的方法了,用來獲取 Subject.

public static Subject getSubject() { Subject subject = ThreadContext.getSubject(); if (subject == null) { subject = (new Subject.Builder()).buildSubject(); ThreadContext.bind(subject); } return subject; } 

上述方法中,第二行(Subject subject = ThreadContext.getSubject();)獲取到的Subject其實是第五行(ThreadContext.bind(subject);)綁定的。

如果沒有之前的綁定則得到null,然后就會走第四行(subject = (new Subject.Builder()).buildSubject();)獲取。步驟如下:

  1. 調用Subject.Builder類的無參構造方法。如下代碼:
    public Builder() { this(SecurityUtils.getSecurityManager()); } 

在這個無參構造方法中,以當前應用全局唯一的SecurityManager對象為參調用了構造方法。如下:

    public Builder(SecurityManager securityManager) { if (securityManager == null) { throw new NullPointerException("SecurityManager method argument cannot be null."); } this.securityManager = securityManager; this.subjectContext = newSubjectContextInstance(); if (this.subjectContext == null) { throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " + "cannot be null."); } this.subjectContext.setSecurityManager(securityManager); } 

其實這些都不重要,至此我們知道了 Subject.Builder 對象中的SecurityManager對象,其實就是當前應用全局唯一的SecurityManager對象。

注:以后在Shiro中,只要看到SecurityManager對象,你就認為它是當前應用全局唯一的那個SecurityManager對象就行了。

  1. 調用 Subject.Builder 對象的buildSubject方法。
    public Subject buildSubject() { return this.securityManager.createSubject(this.subjectContext); } 

其實里面是調用了SecurityManager對象的createSubject方法的,至於那個subjectContext參數,我們可以暫時不用理會。(在不同的應用環境下subjectContext是不一樣的,如Web環境下它默認是DefaultWebSubjectContext

// DefaultSecurityManager 中的 createSubject 方法 public Subject createSubject(SubjectContext subjectContext) { //create a copy so we don't modify the argument's backing map: SubjectContext context = copy(subjectContext); //ensure that the context has a SecurityManager instance, and if not, add one: context = ensureSecurityManager(context); //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the //process is often environment specific - better to shield the SF from these details: context = resolveSession(context); //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first //if possible before handing off to the SubjectFactory: context = resolvePrincipals(context); Subject subject = doCreateSubject(context); //save this subject for future reference if necessary: //(this is needed here in case rememberMe principals were resolved and they need to be stored in the //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation). //Added in 1.2: save(subject); return subject; } 

復制SubjectContext 對象之后執行的 context = ensureSecurityManager(context);是為了確保在SubjectContext對象中已經注入了當前應該用全局唯一的SecurityManager對象:

// DefaultSecurityManager 中的 ensureSecurityManager 方法 protected SubjectContext ensureSecurityManager(SubjectContext context) { if (context.resolveSecurityManager() != null) { log.trace("Context already contains a SecurityManager instance. Returning."); return context; } log.trace("No SecurityManager found in context. Adding self reference."); context.setSecurityManager(this); return context; } 

也就是說,在SubjectContext中的SecurityManager也正是當前應該用全局唯一的SecurityManager對象。

createSubject方法可知,在創建 Subject 前完成了以下兩步工作:

  1. 解析了Sessioncontext = resolveSession(context););
  2. 解析了Principalscontext = resolvePrincipals(context);)。

創建Subject之后,又執行了save(subject);。如果繼續扒代碼你會發現,這一步其實是把Subject存儲到了Session中。具體代碼如下所示:

// DefaultSecurityManager 中的 save 方法 protected void save(Subject subject) { this.subjectDAO.save(subject); } // SubjectDAO 接口的默認實現類 DefaultSubjectDAO 中的 save 方法 public Subject save(Subject subject) { if (isSessionStorageEnabled(subject)) { saveToSession(subject); } else { log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " + "authentication state are expected to be initialized on every request or invocation.", subject); } return subject; } // SubjectDAO 接口的默認實現類 DefaultSubjectDAO 中的 saveToSession方法 protected void saveToSession(Subject subject) { //performs merge logic, only updating the Subject's session if it does not match the current state: mergePrincipals(subject); mergeAuthenticationState(subject); }


作者:JSON_NULL
鏈接:https://www.jianshu.com/p/cf95e3468638
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM