1. 程式人生 > >Shiro原始碼分析(1)

Shiro原始碼分析(1)

簡介

  • 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 {
<span class="hljs-comment">/**
 * 登入
 */</span>
<span class="hljs-function">Subject <span class="hljs-title">login</span><span class="hljs-params">(Subject subject, AuthenticationToken authenticationToken)</span> <span class="hljs-keyword">throws</span> AuthenticationException</span>;

<span class="hljs-comment">/**
 * 登出
 */</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">logout</span><span class="hljs-params">(Subject subject)</span></span>;

<span class="hljs-comment">/**
 * 建立Subject
 */</span>
<span class="hljs-function">Subject <span class="hljs-title">createSubject</span><span class="hljs-params">(SubjectContext context)</span></span>;

}

在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基本上就掌握了。