Shiro 是 Java 世界中新近出現的權限框架,較之 JAAS 和 Spring Security,Shiro 在保持強大功能的同時,還在簡單性和靈活性方面擁有巨大優點。本文介紹了 Shiro 的關鍵概念和權限模型,同時給出了 Shiro 以及 Grails Shiro Plugin 的使用示例。在閱讀本文的過程當中,讀者能夠充分的體會到 Shiro 的魅力。web
前言數據庫
Shiro 是 JAVA 世界中新近出現的權限框架,較之 JAAS 和 Spring Security,Shiro 在保持強大功能的同時,還在簡單性和靈活性方面擁有巨大優點。本文就帶領讀者一睹 Shiro 的風采。緩存
可能你們早先會見過 J-security,這個是 Shiro 的前身。在 2009 年 3 月初以前,這個安全框架叫作 J-security,因爲某些緣由,改名爲 Shiro(或者 Ki,意爲 Fortress),是 Apache 的孵化項目,鑑於本文編寫時 Shiro 的尚未正式發佈的版本,本文使用的是 Jsecurity 的穩定版本 0.9,本文中 Shiro 等同於 Jsecurity。安全
本文將涉及 Shiro 的總體框架、安全模型、關鍵概念類,同時給出了 Shiro 以及 Grails Shiro Plugin 的使用示例,能夠下載文中使用的源代碼。服務器
本文代碼的開發環境:session
Jsecurity 0.9架構
Grails 1.2.0框架
Grails Shiro Plugin 1.0.1ide
SpringSource Tool Suite 2.3工具
Shiro 是一個強大而靈活的開源安全框架,可以很是清晰的處理認證、受權、管理會話以及密碼加密。以下是它所具備的特色:
易於理解的 Java Security API;
簡單的身份認證(登陸),支持多種數據源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
對角色的簡單的籤權(訪問控制),支持細粒度的籤權;
支持一級緩存,以提高應用程序的性能;
內置的基於 POJO 企業會話管理,適用於 Web 以及非 Web 的環境;
異構客戶端會話訪問;
很是簡單的加密 API;
不跟任何的框架或者容器捆綁,能夠獨立運行。
目前還有其餘出現較早的安全框架,好比 JAAS,Spring Security。
JAAS —面世的時間最先,可是鑑於其在使用上有很大的限制,不多有人真正的使用它。能夠說它不是一個好的應用程序級別的安全框架;
Spring Security —目前是 Java 安全框架領域當之無愧的老大,已經很是成熟了;若是使用 Spring 框架,能夠首選 Spring Security,可是對於單應用來講,Shiro 更顯簡單方便。
下面就開始咱們的 Shiro 之旅吧!
總體架構
首先,咱們來看看的 Shiro 的總體架構,見下圖:
圖 1. 總體架構
從上圖能夠看出,Shiro主要有四個組件:
一、SecurityManager
典型的Facade,Shiro經過它對外提供安全管理的各類服務。
二、Authenticator
對"Who are you"實現覈實。一般涉及用戶名和密碼。
這個組件負責收集principals和credentials,並將它們提交給應用系統。吐過提交的credentials跟應用系統中提供的credentials溫和,就能繼續訪問,不然須要從新提交principals和credentials,或者終止訪問。
三、Authorizer
身份驗證經過後,由這個組件對登錄人員進行訪問控制的篩查,好比"Who can do what",或者"Who can 都 which action"。Shiro採用基於Realm的方法,即用戶(又稱爲Subject)、用戶組、角色和permission的聚合體。
四、SessionManager
這個組件保證了異構客戶端的訪問,配置簡單,它是基於POJO/J2SE的,不跟任何的客戶端或者協議綁定。
Shiro的認證和簽證能夠經過JDBC、LDAP 或者 Active Directory 來訪問數據庫、目錄服務器或者 Active Directory 中的人員以及認證 / 籤權信息。SessionManager 經過會話 DAO 能夠將會話保存在 cache 中,或者固化到數據庫或文件系統中。
安全模型
從Shiro的框架圖,已經可以體會到這個工具的簡單。下面讓咱們看看Shiro是如何工做的。
圖 2. 安全模型
上圖,設計了Shiro的五個概念:
一、Subject是安全領域的術語,除了表明人,他還能夠是應用。在單應用中,可將其設爲User的同義詞。
二、Principle是Subject的標識,通常狀況下是惟一的標識,好比用戶名。
三、Role和Permission分別表明了不一樣粒度的權限,從上圖能夠看出Role的粒度更大些,Permission表明了系統的原子權限,好比數據的修改、刪除權限。對於簡單的權限應用,能夠不須要Permission。
四、Realm是一個執行者,負責真正的認證和鑑權。
實現應用的安全模式的關鍵在於:定義合適的role和permission,這就須要遵循以下原則:
一、role沒有實質內容,只是表明了一種permission,目的是爲了管理的方便,通常都是動態定義;
二、permission通常都是預先定義好的,不容許動態改變,除非源碼改動,它纔會變化,它是整個安全模塊的基礎;
三、要是permission也能動態定義,並不是不可能,可是這將鑑權很是複雜,甚至可能致使鑑權語句遍及整個程序,得不償失;
四、固然有一個例外的:若是知道permission動態定義的規則和鑑權規則,如Grail的filter中「${controllerName}:${actionName}:${params.id}」也可實現permission的動態定義。
關鍵概念類
·理解 Shiro 的架構和安全模型了,咱們來看看更具體些的內容。下圖顯示了 Shiro 中的關鍵概念類(參考資料 -- JSecurity Mini Guide)。
圖 3. 關鍵類
AuthenticationToken 和 AuthenticationInfo
前者在認證前使用,描述認證所需的信息,最經常使用的就是 username 和 password 對;後者在認證後使用,內容同前,可是表示已經通過認證的信息。
RememberMe
表明的是一種可能狀態,並不表示該 Subject 已經通過了認證。對於一些普通的操做,這種可能狀態並沒有大礙,但一旦涉及安全敏感的操做,必須通過認證。
Credentials 和 CredentialsMatcher
Credentials 是 Subject 的證書,在認證時使用,最經常使用的就是 password。在一般狀況下,爲了安全起見,Subject 的 credentials 都須要加密保存,因而 CredentialsMatcher 的做用就體現出來了,見下圖:
圖 4. CredentialsMatcher 的做用
這裏 CredentialsMatcher 須要將加密後的證書跟用戶登陸時提供的證書進行比對,完成認證的過程。
PAM= Pluggable Authentication Modules
在有多個 Realm 的時候使用。由認證策略決定認證結果,即 PAM= Relams + 認證策略。通常的策略有 3 種:AllSuccessful、AtLeastOneSuccessful 和 FirstSuccessful。
AuthorizationInfo
能夠當作是 Role + Permission 的組合體。
PermissionResolver 和 Permission
它們之間的關係以下:
圖 5. PermissionResolver 和 Permission 的關係
在 Shiro 中,權限被轉化爲一種字符串描述(字符串分級表示,稱之爲 WildcardPermission),從而將權限轉化爲相似於對象 equals 的操做(Shiro 中的 implies 方法)。
內置的權限有 2 個:
AllPermission,老是返回 true
WildcardPermission,權限字符串的表示方式。
這裏重點聲明一下。WildcardPermission 是 Shiro 的精妙之處,咱們能夠將權限表示成字符串,這樣對權限的控制能夠不拘泥於物理存儲,好比對 messagge 類具備修改和刪除權限能夠標識爲:message:update,delete:*,其中‘ * ’表示全部;第一級分隔符爲‘ : ’;第二級分隔符爲‘ , ’,而對於權限字符串的解釋徹底能夠由應用本身來定。
若是要比較權限字符串,可使用 permission1.implies(permission2),它分別比較對應位置的字符串,在以下狀況中,結果會返回 true:
permission1 中的子串有 * 或 permission1 子串 ==permission2 子串;
permission1 無子串,permission2 有;
permission1 有子串,permission2 無,permission1 的全部子串都是 *。
總的說來,Shiro 中的 Permission 須要注意以下內容:
權限的比較實際是字符串的比較,只不過是考慮到了字符串的分級
字符串的分級劃分徹底由使用者本身決定,Shiro 的慣例是 3 級:資源 : 操做 : 實例。
字符串的使用必須一致,分隔符之間不要有空格,避免無心間引入的不一致。如:定義使用「file : create, update : 1」,而驗證使用「file : update」,那麼分解以後一個是「 update 」,一個是「 update」,因空格而引發不等。
Realm
這是一個實際訪問安全實體的組件,通常是應用相關的,跟數據源的關係是 1-1。它負責完成認證和鑑權,getAuthenticationInfo 表明了 login 的嘗試,鑑權方法則由 Authorizer 繼承而來。此處也體現了 Shiro 代碼的另外一個特色,經過繼承來擴充功能。以經常使用的 JdbcRealm 爲例,其繼承鏈以下:
圖 6. JdbcRealm 的繼承鏈
Session
它關聯一個 Subject 的上下文,其做用相似於在 HttpSession 中保存用戶標識,session 一旦過時,則從新登陸。Shiro 的 Session 是獨立的,其目的是作到環境無關性。爲了利用 Web 環境中,Shiro 實現了一個直接使用 HttpSession 的 WebSession。
SecurityManager
這是一個 Façade 接口,=Authenticator + Authorizer + SessionFactory。在總體框架圖中已經看到了它在 Shiro 中所處的位置。其特色同 Realm,同樣是使用繼承不斷地擴充功能。對於 Web 應用通常使用 DefaultWebSecurityManager。
Filter
在 Web 環境下使用 filter 進行認證和權限檢查是毋庸置疑的,而 Shiro 的特色則在於由一個主 Filter 將一羣子 filter 串起來:
圖 7. Filter 的做用
在實際使用時,須注意:
一、web.xml 中只需配置 JSecurityFilter。對於 Spring 應用,則使用 SpringJSecurityFilter;
二、子 filter 做爲主 filter 的配置參數值出現,特色是:順序相關
對於多個 URL,驗證順序是由上至下,相似 Exception 的匹配。所以,使用順序應該是由細到粗。
對於同一 URL,子 filter 的驗證順序是從左至右的 AND 操做。
三、若是配置值中含有分隔符,如 Permission,就須要使用引號來轉義
Subject
subject 表明了一個用戶的狀態和操做,它提供了全部安全相關的操做,包括認證和籤權。能夠將其視爲另外一種形式的 Façade。缺省實現是將這些操做委派給其內部包含的 SecurityManager。
Configuration
configuration 負責將全部這些組件串起來,最終建立 SecurityManager。在 Shiro 中,缺省格式是 ini。整個配置關係以下圖:
圖 8. 配置關係
其中:
JSecurityFilter 建立 Configuration 實例,並將 ini 參數值傳給 Configuation。在 Spring 環境中,分別使用 SpringJSecurityFilter 和 SpringIniWebConfiguration。
Configuration 實際就是 SecurityManager 的 Factroy,對 SpringIniWebConfiguration 而言,它須要知道 SecurityManager 的 BeanName,該值由 SpringJSecurityFilter 的初始化參數「securityManagerBeanName」值決定。即 SpringJSecurityFilter,實際有兩個初始化參數:
config,是 ini 配置文件內容
securityManagerBeanName,是 SecurityManager 的 BeanName
SecurityUtils
這是 Shiro 中最重要的工具類,由它能夠方便地得到 Subject 和 SecurityManager。
雜項
AOP,提供 AOP 方面的支持,實現對某個類某個方法的攔截,從而使權限控制延伸至類的方法。
Cache,提供緩存支持
Codec,提供編碼方面的支持
Crypto,提供加密支持
IO,從多個資源位置讀寫原始數據
JNDI,提供 jndi 支持
util,工具類支持
標籤類,用於 Web 頁面
注:原文還有基於groovy的例子,在此沒有轉載。