序言
大家好,我是老馬。
前面我們學習了 5 分鐘入門 shiro 安全框架實戰筆記,讓大家對 shiro 有了一個最基本的認識。
shiro 還有其他優秀的特性,今天我們就一起來學習一下,為后續深入學習奠定基礎。
Apache Shiro 是什么?
Apache Shiro 是一種功能強大且易于使用的JAVA安全框架,它執行身份驗證,授權,加密和會話管理,可用于保護任何應用程序的安全-從命令行應用程序,移動應用程序到最大的Web和企業應用程序。
shiro 的組件結構如下圖:
components
Shiro提供了應用程序安全性API來執行以下方面(我喜歡將它們稱為應用程序安全性的4個基石):
- 身份驗證-證明用戶身份,通常稱為用戶“登錄”。
- 授權-訪問控制
- 密碼學-保護或隱藏數據以防窺視
- 會話管理-每個用戶的時間敏感狀態
Shiro還支持一些輔助功能,例如Web應用程序安全性,單元測試和多線程支持,但是這些功能可以增強上述四個主要方面。
特性
您可以使用Apache Shiro進行以下操作:
- 驗證用戶身份以驗證其身份
- 對用戶執行訪問控制,例如:
- 確定是否為用戶分配了特定的安全角色
- 確定是否允許用戶做某事
- 即使在沒有Web或EJB容器的情況下,也可以在任何環境中使用Session API。
- 在身份驗證,訪問控制或會話的生存期內對事件做出反應。
- 匯總1個或更多用戶安全數據的數據源,并將其全部顯示為單個復合用戶“視圖”。
- 啟用單點登錄(SSO)功能
- 啟用“記住我”服務以進行用戶關聯,而無需登錄
為什么要使用 Apache Shiro?
自2003年以來,框架環境發生了很大變化,因此今天仍然有充分的理由使用Shiro。
Apache Shiro 優勢如下:
- 易于使用-易于使用是該項目的最終目標。應用程序安全性可能非常令人困惑和沮喪,并被視為“必不可少的惡魔”。如果您使它易于使用,以使新手程序員可以開始使用它,那么就不必再痛苦了。
- 全面-沒有任何其他安全框架具有Apache Shiro所聲稱的范圍廣度,因此它很可能是滿足您的安全需求的“一站式服務”。
- 靈活-Apache Shiro可以在任何應用程序環境中工作。雖然它可以在Web,EJB和IoC環境中運行,但不需要它們。 Shiro也不要求任何規范,甚至沒有很多依賴性。
- 具有Web功能-Apache Shiro具有出色的Web應用程序支持,可讓您基于應用程序URL和Web協議(例如REST)創建靈活的安全策略,同時還提供了一組JSP庫來控制頁面輸出。
- 可插拔-Shiro簡潔的API和設計模式可輕松與許多其他框架和應用程序集成。您會看到Shiro與Spring,Grails,Wicket,Tapestry,Mule,Apache Camel,Vaadin等框架無縫集成。
- 受支持-Apache Shiro是Apache Software Foundation的一部分,該組織被證明以其社區的最大利益行事。項目開發和用戶群體友好的公民隨時可以提供幫助。如果需要,像Katasoft這樣的商業公司也可以提供專業的支持和服務。
核心概念:Subject,SecurityManager 和 Realms
Shiro的體系結構具有三個主要概念-主題(Subject),安全管理器(SecurityManager)和領域(Realms)。
概念
Subject
在保護應用程序安全時,可能要問自己最相關的問題是:“當前用戶是誰?”或“是否允許當前用戶執行X”?
在編寫代碼或設計用戶界面時,我們通常會問自己以下問題:應用程序通常是基于用戶案例構建的,并且您希望基于每個用戶來表示(和保護)功能。
因此,我們考慮應用程序安全性的最自然方法是基于當前用戶。
Shiro的API從根本上代表了這種思維方式。
“主題”一詞是一個安全術語,基本上表示“當前正在執行的用戶”。它只是不被稱為“用戶”,因為“用戶”一詞通常與人類相關聯。
在安全領域中,“主題”一詞可以表示一個人,但也可以指第三方進程,守護程序帳戶或任何類似內容。它僅表示“當前正在與軟件交互的事物”。
不過,對于大多數意圖和目的,您都可以將其視為Shiro的“用戶”概念。
您可以在代碼中的任何位置輕松獲取Shiro主題,如下面的清單1所示。
- List1
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
...
Subject currentUser = SecurityUtils.getSubject();
獲取主題后,您可以立即訪問當前用戶想要使用Shiro進行的所有操作的90%,例如登錄,注銷,訪問其會話,執行授權檢查等等-但稍后會介紹更多 。
這里的關鍵是Shiro的API在很大程度上是直觀的,因為它反映了開發人員在“每用戶”安全控制中進行思考的自然趨勢。
在代碼中的任何地方訪問主題也很容易,從而可以在需要的地方進行安全操作。
安全管理器
主題的“幕后”對應對象是SecurityManager。
主題代表當前用戶的安全操作,而SecurityManager管理所有用戶的安全操作。它是Shiro體系結構的核心,是一種“傘”對象,它引用了許多內部嵌套的安全組件,這些安全組件構成了一個對象圖。但是,一旦配置了SecurityManager及其內部對象圖,通常就不理會它,應用程序開發人員幾乎將所有時間都花在Subject API上。
那么如何設置SecurityManager?
好吧,這取決于您的應用程序環境。
例如,Web應用程序通常將在web.xml中指定Shiro Servlet過濾器,這將設置SecurityManager實例。如果您運行的是獨立應用程序,則需要以其他方式進行配置。但是有許多配置選項。
每個應用程序幾乎總是只有一個SecurityManager實例。
它本質上是一個應用程序單例(盡管不必是靜態單例)。像Shiro中的幾乎所有內容一樣,默認的SecurityManager實現是POJO,并且可以使用任何與POJO兼容的配置機制-常規Java代碼,Spring XML,YAML,.properties和.ini文件等進行配置。基本上,任何能夠實例化類的東西并且可以使用與JavaBeans兼容的調用方法。
為此,Shiro通過基于文本的INI配置提供了默認的“共母”解決方案。
INI易于閱讀,易于使用,并且幾乎不需要依賴項。您還將看到,通過簡單地了解對象圖導航,可以有效地使用INI來配置簡單的對象圖,例如SecurityManager。
請注意,Shiro還支持Spring XML配置和其他替代方案,但我們將在此處介紹INI。
下面的清單2中的示例顯示了基于INI配置Shiro的最簡單示例。
- List2 使用 INI 配置
[main]
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
# Base64 encoding (less text):
cm.storedCredentialsHexEncoded = false
iniRealm.credentialsMatcher = $cm
[users]
jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2
asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
在清單2中,我們看到了用于配置SecurityManager實例的INI配置示例。
INI分為兩個部分:[main]和[users]。
[main]部分是配置SecurityManager對象和/或SecurityManager使用的任何對象(如領域)的地方。在此示例中,我們看到兩個對象被配置:
cm對象,它是Shiro的HashedCredentialsMatcher類的實例。如您所見,cm實例的各種屬性是通過“嵌套點”語法配置的,該語法由清單3所示的IniSecurityManagerFactory用來表示對象圖導航和屬性設置。
iniRealm對象,是SecurityManager用來表示以INI格式定義的用戶帳戶的組件。
在[用戶]部分中,您可以指定用戶帳戶的靜態列表-適用于簡單的應用程序或測試時。
出于本簡介的目的,理解每一部分的復雜性并不重要,而是要了解INI配置是配置Shiro的一種簡單方法。
有關INI配置的更多詳細信息,請參閱Shiro的文檔。
- List3 加載配置文件
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.Factory;
//1. Load the INI configuration
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
//2. Create the SecurityManager
SecurityManager securityManager = factory.getInstance();
//3. Make it accessible
SecurityUtils.setSecurityManager(securityManager);
在清單3中,在這個簡單的示例中,我們看到了一個三步過程:
- 加載將配置SecurityManager及其組成組件的INI配置。
- 根據配置(使用代表工廠方法設計模式的Shiro的工廠概念)創建SecurityManager實例。
- 使SecurityManager單例可被應用程序訪問。
在這個簡單的示例中,我們將其設置為VM靜態單例,但這通常不是必需的-您的應用程序配置機制可以確定是否需要使用靜態內存。
Realms(領域)
Shiro中的第三個也是最后一個核心概念是領域。
領域充當Shiro與應用程序的安全數據之間的“橋梁”或“連接器”。
也就是說,當需要真正與安全性相關的數據(例如用戶帳戶)進行交互以執行身份驗證(登錄)和授權(訪問控制)時,Shiro會從為一個應用程序配置的一個或多個Realms中查找其中的許多內容。
從這個意義上說,領域本質上是特定于安全性的DAO:它封裝了數據源的連接詳細信息,并根據需要使關聯數據可用于Shiro。在配置Shiro時,您必須至少指定一個領域用于身份驗證和/或授權。可以配置多個Realm,但至少需要一個。
Shiro提供了開箱即用的領域,可以連接到許多安全數據源(又名目錄),例如LDAP,關系數據庫(JDBC),文本配置源(例如INI和屬性文件)等等。
如果默認的Realms不能滿足您的需求,那么您可以插入自己的Realm實現以表示自定義數據源。
下面的清單4是配置Shiro(通過INI)以將LDAP目錄用作應用程序的領域之一的示例。
- 清單4.示例領域配置
[main]
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
ldapRealm.contextFactory.url = ldap://ldapHost:389
ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
現在,我們已經了解了如何設置基本的Shiro環境,讓我們討論您作為開發人員如何使用該框架。
詳細架構
下圖顯示了Shiro的核心架構概念,并簡要概述了每個架構:
詳細架構
下面我們對除了核心組件的部分做一下簡單的介紹:
Authentication(身份驗證)
身份驗證是驗證用戶身份的過程。
也就是說,當用戶通過應用程序進行身份驗證時,他們在證明自己實際上就是他們所說的身份。有時也稱為“登錄”。
這通常是一個三步過程。
- 收集用戶的識別信息(稱為主體)和支持身份證明的憑據(稱為憑據)。
- 將主體和憑據提交到系統。
- 如果提交的憑據與系統對該用戶身份(本金)的期望匹配,則認為該用戶已通過身份驗證。如果不匹配,則認為該用戶未通過身份驗證。
每個人都熟悉的此過程的一個常見示例是用戶名/密碼組合。當大多數用戶登錄軟件應用程序時,通常會提供其用戶名(主體)和支持密碼(憑據)。如果存儲在系統中的密碼(或密碼表示形式)與用戶指定的密碼匹配,則認為它們已通過身份驗證。
Shiro以簡單直觀的方式支持相同的工作流程。
正如我們所說,Shiro具有以主題為中心的API-在運行時,您與Shiro所做的幾乎所有事情都是通過與當前正在執行的Subject進行交互來實現的。
因此,要登錄主題,您只需調用其登錄方法,并傳遞一個AuthenticationToken實例,該實例代表所提交的主體和憑據(在本例中為用戶名和密碼)。
此示例在下面的清單5中顯示。
- 列表5 主題登錄
//1. Acquire submitted principals and credentials:
AuthenticationToken token =
new UsernamePasswordToken(username, password);
//2. Get the current Subject:
Subject currentUser = SecurityUtils.getSubject();
//3. Login:
currentUser.login(token);
如您所見,Shiro的API可以輕松反映常見的工作流程。
您會繼續將這種簡單性視為主題操作的所有操作的主題。
當調用登錄方法時,SecurityManager將接收AuthenticationToken并將其分發給一個或多個配置的領域,以允許每個領域根據需要執行身份驗證檢查。
每個領域都可以根據需要對提交的AuthenticationToken做出反應。
但是,如果登錄嘗試失敗會怎樣?
如果用戶指定了錯誤的密碼怎么辦?
您可以通過對Shiro的運行時AuthenticationException做出反應來處理故障,如清單6所示。
- 列表6-處理失敗的場景
//3. Login:
try {
currentUser.login(token);
} catch (IncorrectCredentialsException ice) { …
} catch (LockedAccountException lae) { …
}
…
catch (AuthenticationException ae) {…
}
您可以選擇捕獲AuthenticationException子類之一并作出具體反應,或者一般性地處理任何AuthenticationException(例如,向用戶顯示通用的“用戶名或密碼錯誤”消息)。
選擇取決于您,具體取決于您的應用程序需求。
主題成功登錄后,它們被認為已通過身份驗證,通常您允許他們使用您的應用程序。
但是,僅僅因為用戶證明了自己的身份并不意味著他們可以在應用程序中做任何想做的事情。
這就引出了下一個問題:“如何控制允許用戶執行或不執行的操作?” 確定允許用戶執行的操作稱為授權。
接下來,我們將介紹Shiro如何啟用授權。
Authorization(授權)
授權本質上是訪問控制-控制用戶可以在應用程序中訪問的內容(例如資源,網頁等)。
大多數用戶通過使用角色和權限等概念來執行訪問控制。
也就是說,通常根據分配給他們的角色和/或權限,允許用戶執行某項操作或不執行某項操作。
然后,您的應用程序可以根據對這些角色和權限的檢查來控制公開哪些功能。
如您所料,主題API使您可以非常輕松地執行角色和權限檢查。
例如,清單7中的代碼片段顯示了如何檢查Subject是否被分配了特定角色。
- 清單7-角色檢查
if ( subject.hasRole("administrator") ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
如您所見,您的應用程序可以基于訪問控制檢查來啟用或禁用功能。
權限檢查是執行授權的另一種方法。
如上例所示,檢查角色存在一個重大缺陷:您無法在運行時添加或刪除角色。您的代碼使用角色名稱進行了硬編碼,因此,如果更改了角色名稱和/或配置,則代碼將被破壞!如果您需要能夠在運行時更改角色的含義,或者根據需要添加或刪除角色,則必須依靠其他方式。
為此,Shiro支持其權限概念。
權限是對功能的原始說明,例如“開門”,“創建博客條目”,“刪除'jsmith'用戶”等。
通過使權限反映您的應用程序的原始功能,您只需更改權限檢查何時更改應用程序的功能。反過來,您可以在運行時根據需要向角色或用戶分配權限。
作為一個示例,如下面的清單8所示,我們可以重寫之前的角色檢查,而使用權限檢查。
- 清單8-權限檢測
if (subject.isPermitted("user:create") ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
這樣,分配了 user:create 權限的任何角色或用戶都可以單擊“Create User”按鈕,并且這些角色和分配甚至可以在運行時更改,從而為您提供了非常靈活的安全模型。
“usercreate”字符串是遵循某些解析約定的權限字符串的示例。
Shiro的WildcardPermission支持此約定。
盡管不在本介紹文章的討論范圍之內,但您會發現WildcardPermission在創建安全策略時可以非常靈活,甚至還支持實例級訪問控制之類的功能。
- 清單9-實例級別權限校驗
if ( subject.isPermitted(“user:delete:jsmith”) ) {
//delete the ‘jsmith’ user
} else {
//don’t delete ‘jsmith’
}
此示例表明,如果需要,您可以控制甚至訪問非常細致的實例級別。
如果愿意,您甚至可以發明自己的權限語法。
有關更多信息,請參見 Shiro權限文檔。
最后,與身份驗證一樣,上述調用最終也進入了SecurityManager,后者將咨詢一個或多個Realms來做出訪問控制決策。 這使領域可以根據需要響應身份驗證和授權操作。
這就是Shiro授權功能的簡要概述。
盡管大多數安全框架都停止了身份驗證和授權,但Shiro提供了更多功能。
接下來,我們將討論Shiro的高級會話管理功能。
會話管理
Apache Shiro在安全框架領域提供了一些獨特的功能:可在任何應用程序和任何體系結構層中使用的一致的Session API。
也就是說,Shiro為任何應用程序啟用了會話編程范例-從小型守護程序獨立應用程序到最大的群集Web應用程序。
這意味著希望使用會話的應用程序開發人員不再需要,則不再需要使用Servlet或EJB容器。或者,如果使用這些容器,則開發人員現在可以選擇在任何層中使用統一且一致的會話API,而不是使用servlet或EJB特定的機制。
但是Shiro會話的最重要好處之一就是它們與容器無關。
這具有微妙但極其強大的含義。
例如,讓我們考慮會話集群。有多少種特定于容器的方式可以將會話群集在一起以實現容錯和故障轉移?
Tomcat與Jetty的功能不同,而Jetty與Websphere的功能不同。但是,通過Shiro會話,您可以獲得獨立于容器的集群解決方案。
Shiro的體系結構允許可插入的Session數據存儲,例如企業緩存,關系數據庫,NoSQL系統等等。
這意味著您只需配置一次會話集群,無論部署環境如何(Tomcat,Jetty,JEE Server或獨立應用程序),它都將以相同的方式工作。
無需根據部署應用程序的方式重新配置應用程序。
Shiro會話的另一個好處是,如果需要,會話數據可以跨客戶端技術共享。
例如,如果需要,Swing桌面客戶端可以參加相同的Web應用程序會話-如果最終用戶同時使用這兩個客戶端,則很有用。
那么,您如何在任何環境中訪問主題的會話?
如下面的示例所示,有兩種Subject方法。
- Listing 10. Subject’s Session
Session session = subject.getSession();
Session session = subject.getSession(boolean create);
如您所見,這些方法在概念上與HttpServletRequest API相同。
第一種方法將返回主題的現有會話,或者如果沒有,則創建一個新的會話并返回。
第二種方法接受一個布爾參數,該參數確定是否將創建一個新的會話(如果尚不存在)。
獲取主題的會話后,就可以將其幾乎與HttpSession一樣使用。
Shiro團隊認為HttpSession API最適合Java開發人員,因此我們保留了大部分感覺。
當然,最大的區別是您可以在任何應用程序中使用Shiro Sessions,而不僅僅是Web應用程序。
清單11顯示了這種熟悉程度。
- Listing 11. Session methods
Session session = subject.getSession();
session.getAttribute("key", someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);
密碼學
密碼術是隱藏或混淆數據的過程,因此窺探眼睛無法理解它。
Shiro的加密目標是簡化JDK的加密支持并使之可用。
需要特別注意的是,密碼通常不是特定于主題的,因此Shiro API的其中一個領域不是特定于主題的。
即使未使用“主題”,您也可以在任何地方使用Shiro的加密支持。
Shiro真正側重于其加密支持的兩個領域是加密哈希(又名消息摘要)和加密密碼領域。
讓我們更詳細地看看這兩個。
散列
如果您使用了JDK的MessageDigest類,您很快就會意識到使用它有點麻煩。它具有笨拙的基于靜態方法的基于工廠的API,而不是面向對象的API,因此您不得不捕獲可能永遠不需要捕獲的已檢查異常。如果您需要十六進制編碼或Base64編碼的消息摘要輸出,則由您自己決定-兩者均不提供標準的JDK支持。
Shiro通過干凈直觀的哈希API解決了這些問題。
例如,讓我們考慮MD5散列文件并確定該散列的十六進制值的相對常見的情況。稱為“校驗和”,通常在提供文件下載時使用-用戶可以對下載的文件執行自己的MD5哈希,并斷言其校驗和與下載站點上的校驗和匹配。如果它們匹配,則用戶可以充分假設文件在傳輸過程中未被篡改。
在沒有Shiro的情況下,您可以嘗試以下操作:
- 將文件轉換為字節數組。 JDK中沒有任何東西可以幫助您解決此問題,因此您需要創建一個輔助方法,該方法可以打開FileInputStream,使用字節緩沖區并拋出適當的IOException等。
- 使用MessageDigest類對字節數組進行哈希處理,以處理適當的異常,如下面的清單12所示。
- 將散列字節數組編碼為十六進制字符。 JDK中也沒有任何東西可以提供幫助,因此您需要創建另一個幫助器方法,并可能在實現中使用按位運算和移位。
- Listing 12. JDK’s MessageDigest
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.digest(bytes);
byte[] hashed = md.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
對于如此簡單且相對常見的事物而言,這是一項巨大的工作。
現在,說明如何使用Shiro進行完全相同的操作。
String hex = new Md5Hash(myFile).toHex();
使用Shiro簡化所有工作時,了解正在發生的事情非常簡單和容易。
SHA-512哈希和密碼的Base64編碼也很容易。
String encodedPassword =
new Sha512Hash(password, salt, count).toBase64();
您會看到Shiro在很大程度上簡化了哈希和編碼,從而在此過程中節省了一些理智。
密碼
密碼是可以使用密鑰可逆地轉換數據的密碼算法。我們使用它們來保護數據安全,尤其是在傳輸或存儲數據時,尤其是在數據容易被撬開的時候。
如果您曾經使用過JDK密碼API,尤其是javax.crypto.Cipher類,那么您就會知道,馴服它可能是一件極其復雜的事情。對于初學者來說,每種可能的Cipher配置始終由javax.crypto.Cipher的實例表示。
需要做公鑰/私鑰加密嗎?
您使用密碼。是否需要使用分組密碼進行流操作?您使用密碼。是否需要創建AES 256位密碼來保護數據?您使用密碼。你明白了。
以及如何創建所需的Cipher實例?
您創建了一個復雜的,不直觀的,用令牌分隔的密碼選項字符串,稱為“轉換字符串”,并將該字符串傳遞給Cipher.getInstance靜態工廠方法。使用這種密碼選項字符串方法,沒有類型安全性來確保您使用有效的選項。這也隱含地意味著沒有JavaDoc可以幫助您了解相關選項。而且,即使您知道配置正確,也需要處理經過檢查的異常,以防您的字符串格式錯誤。如您所見,使用JDK Ciphers是一項繁瑣的任務。這些技術很久以前曾經是Java API的標準,但是時代已經改變,我們希望有一種更簡單的方法。
Shiro試圖通過引入其CipherService API簡化整個密碼算法的概念。
大多數開發人員在保護數據時都希望使用CipherService:一種簡單,無狀態,線程安全的API,可以在一個方法調用中完整地加密或解密數據。您所需要做的就是提供密鑰,然后可以根據需要加密或解密。
例如,可以使用256位AES加密,如下面的清單13所示。
- Listing 13. Apache Shiro’s Encryption API
AesCipherService cipherService = new AesCipherService();
cipherService.setKeySize(256);
//create a test key:
byte[] testKey = cipherService.generateNewKey();
//encrypt a file’s bytes:
byte[] encrypted =
cipherService.encrypt(fileBytes, testKey);
與JDK的Cipher API相比,Shiro示例更簡單:
- 您可以直接實例化CipherService-沒有奇怪或令人困惑的工廠方法。
- 密碼配置選項表示為與JavaBeans兼容的getter和setter-沒有奇怪且難以理解的“轉換字符串”。
- 加密和解密在單個方法調用中執行。
- 沒有強制檢查的異常。 如果需要,請捕獲Shiro的CryptoException。
Shiro的CipherService API還有其他好處,例如既支持基于字節數組的加密/解密(稱為“塊”操作),又支持基于流的加密/解密(例如,加密音頻或視頻)。
Java密碼術不必太痛苦。
Shiro的密碼學支持旨在簡化您保護數據安全的工作。
web 支持
最后但并非最不重要的一點,我們將簡要介紹Shiro的 web 支持。
Shiro隨附了強大的Web支持模塊,以幫助保護Web應用程序。
為Web應用程序設置Shiro很簡單。 唯一需要做的就是在web.xml中定義一個Shiro Servlet過濾器。
清單14顯示了此代碼。
- Listing 14. ShiroFilter in web.xml
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>
org.apache.shiro.web.servlet.IniShiroFilter
</filter-class>
<!-- no init-param means load the INI config
from classpath:shiro.ini -->
</filter>
<filter-mApping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
該過濾器可以讀取上述的shiro.ini配置,因此無論部署環境如何,您都可以獲得一致的配置體驗。
配置完成后,Shiro篩選器將篩選每個請求,并確保在請求期間可訪問特定于請求的主題。
并且因為它過濾了每個請求,所以您可以執行特定于安全性的邏輯,以確保僅允許滿足特定條件的請求通過。
URL特定的過濾器鏈
Shiro通過其創新的URL過濾器鏈接功能支持特定于安全性的過濾器規則。
它允許您為任何匹配的URL模式指定臨時過濾器鏈。
這意味著您在使用Shiro的過濾機制執行安全規則(或規則組合)方面具有很大的靈活性-比僅在web.xml中定義過濾器要強得多。
清單15顯示了Shiro INI中的配置代碼段。
- Listing 15. Path-specific Filter Chains
[urls]
/assets/** = anon
/user/signup = anon
/user/** = user
/rpc/rest/** = perms[rpc:invoke], authc
/** = authc
如您所見,Web應用程序有一個[urls] INI部分。對于每一行,等號左側的值代表上下文相關的Web應用程序路徑。右側的值定義了一個Filter鏈-要對給定路徑執行的Servlet過濾器的列表,以逗號分隔。
每個過濾器都是一個普通的Servlet過濾器,但是您在上面看到的過濾器名稱(匿名,用戶,權限,身份驗證)是Shiro提供的與安全相關的特殊過濾器。
您可以混合使用這些安全過濾器,以創建非常自定義的安全體驗。您還可以指定可能具有的任何其他現有Servlet過濾器。
與使用web.xml(定義一個過濾器塊,然后定義一個分離的過濾器模式塊)相比,這要好多少?
使用Shiro的方法,可以更輕松地準確查看針對給定匹配路徑執行的過濾器鏈。
如果需要,可以在web.xml中僅定義Shiro過濾器,并在shiro.ini中定義所有其他過濾器和過濾器鏈,以使過濾器鏈定義機制比web.xml更加簡潔明了。
即使您沒有使用Shiro的任何安全功能,僅此一項小小的便利都可以使Shiro值得使用。
JSP標簽庫
Shiro還提供了一個JSP標記庫,通過該庫,您可以根據當前Subject的狀態來控制JSP頁面的輸出。
一個有用的常見示例是在用戶登錄后顯示 Hello <username> 文本。
但是,如果它們是匿名的,則可能需要顯示其他內容,例如 Hello! Register Today! 代替。
清單16顯示了如何使用Shiro的JSP標簽支持這一點。
- Listing 16. JSP Taglib
<%@ taglib prefix="shiro"
uri="http://shiro.apache.org/tags" %>
...
<p>Hello
<shiro:user>
<!-- shiro:principal prints out the Subject’s main
principal - in this case, a username: -->
<shiro:principal/>!
</shiro:user>
<shiro:guest>
<!-- not logged in - considered a guest. Show
the register link: -->
! <a href=”register.jsp”>Register today!</a>
</shiro:guest>
</p>
還有其他標簽可讓您根據其擁有(或沒有)的角色,分配(或未分配)哪些權限以及是否對它們進行身份驗證,從“記住我”服務中記住這些權限或 匿名訪客。
Shiro還支持許多其他特定于Web的功能,例如簡單的“ Remember Me”服務,REST和BASIC身份驗證,當然,如果要使用Shiro的本機企業會話,則當然還支持透明的HttpSession支持。
web 會話管理
最后,指出Shiro對網絡環境中的會話的支持很有趣。
默認Http會話
對于Web應用程序,Shiro將其會話基礎結構默認為使用我們都習慣的現有Servlet容器會話。
也就是說,當您調用方法subject.getSession()和subject.getSession(boolean)時,Shiro將返回由Servlet容器的HttpSession實例支持的Session實例。
這種方法的優點在于,調用subject.getSession()的業務層代碼與Shiro Session實例進行交互-它不具備與基于Web的HttpSession對象一起工作的“知識”。
在跨架構層保持清晰隔離時,這是一件非常好的事情。
Web層中Shiro的本機會話
如果您由于需要Shiro的企業會話功能(例如與容器無關的集群)而在Web應用程序中啟用了Shiro的本機會話管理,那么您當然希望HttpServletRequest.getSession()和HttpSession API與“本機”會話一起使用,并且而不是Servlet容器會話。
如果您必須重構任何使用HttpServletRequest和HttpSession API的代碼來替代使用Shiro的Session API,那將非常令人沮喪。
Shiro當然不會期望您這樣做。
相反,Shiro完全實現了Servlet規范的Session部分,以支持Web應用程序中的本機會話。
這意味著無論何時調用相應的HttpServletRequest或HttpSession方法調用,Shiro都會將這些調用委派給其內部的本地Session API。
最終結果是,即使您使用的是Shiro的“本地”企業會話管理,也不必更改Web代碼-確實是非常方便(且必不可少)的功能。
附加功能
Apache Shiro框架中還有其他對保護Java應用程序有用的功能,例如:
1, 線程和并發支持,用于跨線程維護主題(Executor和ExecutorService支持)
- 可調用和可運行支持將邏輯作為特定主題執行
- “運行方式”支持,用于假設另一個主題的身份(例如在管理應用程序中很有用)
- 測試工具支持,使在單元測試和集成測試中對Shiro安全代碼進行全面測試變得非常容易
框架局限性
就像我們希望的那樣,Apache Shiro并不是“銀彈”-它不會輕松解決所有安全問題。
Shiro無法解決的某些事情可能值得了解:
虛擬機級別的問題
Apache Shiro當前不處理虛擬機級別的安全性,例如基于訪問控制策略阻止某些類加載到類加載器中的能力。
但是,Shiro可以與現有的JVM安全操作集成是不可想象的-只是沒有人為該項目做出過這樣的貢獻。
多階段身份驗證
Shiro當前不本地支持“多階段”身份驗證,在這種情況下,用戶可能通過一種機制登錄,只是被要求然后使用另一種機制再次登錄。
這已在基于Shiro的應用程序中完成,但是該應用程序會預先收集所有必需的信息,然后與Shiro進行交互。很有可能在將來的Shiro版本中支持此功能。
領域寫入操作
目前,所有Realm實施都支持“讀取”操作,以獲取身份驗證和授權數據以執行登錄和訪問控制。
不支持“寫入”操作,例如創建用戶帳戶,組合角色,或將用戶與角色組和權限相關聯。這是因為支持這些操作的數據模型在不同的應用程序中差異很大,并且很難在所有Shiro用戶上強制執行“寫入” API。
小結
Apache Shiro 是功能齊全,健壯且通用的Java安全框架,可用于保護應用程序安全。
通過簡化應用程序安全性的四個領域,即身份驗證,授權,會話管理和密碼學,可以更輕松地在實際應用程序中理解和實現應用程序安全性。
希望本文對你有所幫助,如果喜歡,歡迎點贊收藏轉發一波。
我是老馬,期待與你的下次相遇。