簡介
- SecurityManager:安全管理器,Shiro最核心組件。Shiro通過SecurityManager來管理內部組件實例,並通過它來提供安全管理的各種服務。
- Authenticator:認證器,認證AuthenticationToken是否有效。
- Authorizer:授權器,處理角色和權限。
- SessionManager:Session管理器,管理Session。
- Subject:當前操作主體,表示當前操作用戶。
- SubjectContext:Subject上下文數據對象。
- AuthenticationToken:認證的token信息(用戶名、密碼等)。
- ThreadContext:線程上下文對象,負責綁定對象到當前線程。
在學習和使用Shiro過程中,我們都知道SecurityManager接口在Shiro中是最為核心的接口。我們就沿着這個接口進行分析。
下面的代碼是SecurityManager接口的定義:
public interface SecurityManager extends Authenticator, Authorizer, SessionManager { /** * 登錄 */ Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException; /** * 登出 */ void logout(Subject subject); /** * 創建Subject */ Subject createSubject(SubjectContext context); }
在SecurityManager 中定義了三個方法,分別是登錄、登出和創建Subject。通常我們在使用的時候是這樣使用的。首先創建Subject對象,然后通過調用login方法傳入認證信息token對登錄進行認證。
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); subject.login(token);
SecurityUtils分析
在Shiro中提供了一個方便使用的工具類SecurityUtils,SecurityUtils核心功能是獲取SecurityManager以及Subject。這兩個接口是Shiro提供的外圍接口,供開發時使用。
在SecurityUtils使用static定義SecurityManager,也就是說SecurityManager對象在應用中是單一存在的。
private static SecurityManager securityManager;
1. 獲取SecurityManager
首先從ThreadContext中獲取,如果沒有,則從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; }
2. 獲取Subject
首先從ThreadContext中獲取,如果不存在,則創建新的Subject,再存放到ThreadContext中,以便下次可以獲取。
public static Subject getSubject() { Subject subject = ThreadContext.getSubject(); if (subject == null) { subject = (new Subject.Builder()).buildSubject(); ThreadContext.bind(subject); } return subject; }
在上面的代碼中重要是通過 Subject.Builder類提供的buildSubject()方法來創建Subject。在創建Subject時同時還創建了SubjectContext對象,也就是說Subject和SubjectContext是一一對應的。下面的代碼是Subject.Builder類的構造方法。
public Builder(SecurityManager securityManager) { if (securityManager == null) { throw new NullPointerException("SecurityManager method argument cannot be null."); } this.securityManager = securityManager; // 創建了SubjectContext實例對象 this.subjectContext = newSubjectContextInstance(); if (this.subjectContext == null) { throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " + "cannot be null."); } this.subjectContext.setSecurityManager(securityManager); }
而buildSubject()方法則實際上是調用SecurityManager接口中的createSubject(SubjectContext subjectContext)方法。
public Subject buildSubject() { return this.securityManager.createSubject(this.subjectContext); }
總結
本篇主要通過SecurityUtils.getSubject()對SecurityManager接口中的createSubject(SubjectContext subjectContext)方法進行了詳細的分析。另外兩個方法我們在分析Subject時做詳細分析。
另外,我們會發現SecurityManager繼承了 Authenticator, Authorizer, SessionManager三個接口,這樣才能實現SecurityManager提供安全管理的各種服務。在接下來的文章中會對Authenticator, Authorizer, SessionManager分別進行分析,這樣我們對SecurityManager基本上就掌握了。