博客 / 詳情

返回

Shiro中的核心概念(翻譯)

Apache Shiro™ 是一個功能強大且易於使用的 Java 安全框架,用於執行身份驗證、授權、加密和會話管理。藉助 Shiro 易於理解的 API,您可以快速輕鬆地保護任何應用程序 - 從最小的移動應用程序到最大的 Web 和企業應用程序。

一、什麼是 Apache Shiro?

Apache Shiro(發音為“shee-roh”,日語中“城堡”的意思)是一個功能強大且易於使用的 Java 安全框架,它執行身份驗證、授權、加密和會話管理,可用於保護任何應用程序的安全 -從命令行應用程序、移動應用程序到最大的 Web 和企業應用程序。

Shiro 提供了應用程序安全 API 來執行以下方面(我喜歡將它們稱為應用程序安全性的 4 個基石):

  • Authentication - 提供用户的身份認證,通常被稱為用户登錄。
  • Authorization - 訪問控制
  • Cryptography - 保護或隱藏數據免遭窺探
  • Session Management - 每個用户的時間敏感狀態

Shiro 還支持一些輔助功能,例如 Web 應用程序安全性、單元測試和多線程支持,但這些功能的存在是為了強化上述四個主要問題。

二、為什麼創建Apache Shiro?

對於一個框架來説,要真正證明它的存在,並因此成為你使用它的理由,它應該滿足其他替代方案無法滿足的需求。為了理解這一點,我們需要看看 Shiro 的歷史以及它創建時的替代方案。

在 2008 年加入 Apache 軟件基金會之前,Shiro 已經有 5 歲了,之前稱為 JSecurity 項目,該項目於 2003 年初啓動。2003 年,Java 應用程序開發人員沒有太多通用的安全替代方案 - 我們幾乎只能使用 Java 身份驗證和授權服務,也稱為 JAAS。JAAS 有很多缺點——雖然它的身份驗證功能還可以接受,但授權方面卻很遲鈍,使用起來令人沮喪。此外,JAAS 與虛擬機級別的安全問題密切相關,例如,確定是否應允許在 JVM 中加載類。作為一名應用程序開發人員,我更關心應用程序最終用户可以做什麼,而不是我的代碼在 JVM 中可以做什麼。當時遊戲中唯一的會話選擇是 HttpSession(需要 Web 容器)和 EJB 2.1 Stateful Session Beans(需要 EJB 容器)。

由於我當時使用的應用程序,我還需要訪問一個乾淨的、與容器無關的會話機制。我需要一些可以與容器分離的東西,可以在我選擇的任何環境中使用。

最後是密碼學問題。有時我們都需要保證數據安全,但 Java 加密架構很難理解,除非你是加密專家。該 API 充滿了檢查異常,使用起來很麻煩。我希望有一個更乾淨、開箱即用的解決方案,可以根據需要輕鬆加密和解密數據。

因此,看看 2003 年初的安全形勢,您很快就會意識到沒有任何東西可以在一個單一的、有凝聚力的框架中滿足所有這些要求。因此,JSecurity 以及後來的 Apache Shiro 誕生了。

三、為什麼今天要使用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 軟件基金會的一部分,事實證明該組織的行為符合社區的最佳利益。項目開發和用户團體有友好的人員隨時準備提供幫助。如果需要,Katasoft 等商業公司也會提供專業支持和服務。

四、誰在使用Shiro?

Shiro 及其前身 JSecurity 已在各種規模和跨行業的公司項目中使用多年。自從成為 Apache 軟件基金會頂級項目以來,網站流量和採用率持續顯着增長。許多開源社區也在使用 Shiro,例如 Spring、Grails、Wicket、Tapestry、Tynamo、Mule 和 Vaadin,僅舉幾例。

Katasoft、Sonatype、主要社交網絡之一的 MuleSoft 等商業公司以及多家紐約商業銀行都使用 Shiro 來保護其商業軟件和網站的安全。

五、核心概念:Subject、SecurityManager和Realms

現在我們已經介紹了 Shiro 的優點,讓我們直接進入它的 API,以便您可以感受一下它。 Shiro 的架構具有三個主要概念——Subject、SecurityManager 和 Realms。

  • Subject

    當你保護你的應用程序時,可能要問自己最相關的問題是:"誰是當前用户?"或 "當前用户是否被允許做X操作"?我們在編寫代碼或設計用户界面時,經常會問自己這些問題:應用程序通常是基於用户故事構建的,並且您希望基於每個用户來表示(和保護)功能。因此,我們考慮應用程序安全性的最自然方式是基於當前用户。Shiro 的 API 從根本上體現了其主題概念中的這種思維方式。

    “主題”一詞是一個安全術語,基本上意味着“當前正在執行的用户”。它只是不被稱為“用户”,因為“用户”一詞通常與人類相關聯。在安全領域,術語“主題”可以指人類,也可以指第 3 方進程、守護程序帳户或任何類似的東西。它只是意味着“當前正在與軟件交互的事物”。不過,對於大多數意圖和目的,您可以將其視為 Shiro 的“用户”概念。您可以輕鬆地在代碼中的任何位置獲取 Shiro 主題,如下面的清單 1 所示。

    一旦您獲取了主題,您就可以立即訪問當前用户想要使用 Shiro 執行的 90% 的操作,例如登錄、註銷、訪問其會話、執行授權檢查等等 - 稍後會詳細介紹。這裏的關鍵點是 Shiro 的 API 很大程度上是直觀的,因為它反映了開發人員思考“每個用户”安全控制的自然傾向。在代碼中的任何位置訪問主題也很容易,從而允許安全操作在需要的任何地方進行。

    清單1:獲取主題

    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.SecurityUtils;
    ...
    Subject currentUser = SecurityUtils.getSubject();
  • SecurityManager

    主題的“幕後”對應者是 SecurityManager。雖然主題代表當前用户的安全操作,但安全管理器管理所有用户的安全操作。它是 Shiro 架構的核心,充當一種“傘”對象,引用許多形成對象圖的內部嵌套安全組件。然而,一旦配置了 SecurityManager 及其內部對象圖,通常就不再管它了,應用程序開發人員幾乎將所有時間都花在了主題 API 上。

    那麼如何設置SecurityManager呢?嗯,這取決於你的應用環境。例如,Web 應用程序通常會在 web.xml 中指定 Shiro Servlet Filter,這將設置 SecurityManager 實例。如果您運行的是獨立應用程序,則需要以其他方式配置它。但有很多配置選項。

    每個應用程序幾乎總是有一個 SecurityManager 實例。它本質上是一個應用程序單例(儘管它不需要是靜態單例)。與 Shiro 中的幾乎所有內容一樣,默認的 SecurityManager 實現是 POJO,並且可以使用任何 POJO 兼容的配置機制進行配置 - 普通 Java 代碼、Spring XML、YAML、.properties 和 .ini 文件等。基本上,任何能夠實例化類並調用 JavaBeans 兼容方法的東西都可以使用。

    為此,Shiro 通過基於文本的 INI 配置提供了默認的“共同點”解決方案。INI 易於閲讀、使用簡單,並且需要很少的依賴項。您還將看到,通過對對象圖導航的簡單理解,可以有效地使用 INI 來配置簡單的對象圖,例如 SecurityManager。請注意,Shiro 還支持 Spring XML 配置和其他替代方案,但我們將在這裏介紹 INI。基於 INI 配置 Shiro 的最簡單示例如下面的清單 2 中的示例所示。

    清單2:使用 INI 配置 Shiro

    [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使用的任何對象(如Realms)。在此示例中,我們看到正在配置兩個對象:

    1. cm 對象,它是 Shiro 的 HashedCredentialsMatcher 類的實例。正如您所看到的,cm 實例的各種屬性是通過“嵌套點”語法(清單 3 中所示的 IniSecurityManagerFactory 使用的約定)進行配置的,用於表示對象圖導航和屬性設置。
    2. iniRealm 對象,它是 SecurityManager 用來表示以 INI 格式定義的用户帳户的組件。

    在 [users] 部分,您可以指定用户帳户的靜態列表 - 方便簡單的應用程序或測試時。

    出於本介紹的目的,理解每個部分的複雜性並不重要,重要的是要了解 INI 配置是配置 Shiro 的一種簡單方法。有關 INI 配置的更多詳細信息,請參閲 Shiro 的文檔。

    清單3:加載shiro.ini配置文件

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.util.Factory;
    
    // 加載INI配置
    Factory<SecurityManager> factory =
    new IniSecurityManagerFactory("classpath:shiro.ini");
    // 創建SecurityManager
    SecurityManager securityManager = factory.getInstance();
    // 使其易於訪問
    SecurityUtils.setSecurityManager(securityManager);
    
    

    在清單 3 中,我們在這個簡單示例中看到了一個三步過程:

    1. 加載將配置 SecurityManager 及其組成組件的 INI 配置。
    2. 根據配置創建 SecurityManager 實例(使用代表工廠方法設計模式的 Shiro 工廠概念)。
    3. 使 SecurityManager 單例可供應用程序訪問。在這個簡單的示例中,我們將其設置為 VM 靜態單例,但這通常是不必要的 - 您的應用程序配置機制可以確定您是否需要使用靜態內存。
  • Realms

    Shiro 中的第三個也是最後一個核心概念是Realm。Realm 充當 Shiro 和應用程序安全數據之間的“橋樑”或“連接器”。也就是説,當實際與安全相關數據(例如用户帳户)進行交互以執行身份驗證(登錄)和授權(訪問控制)時,Shiro 從為應用程序配置的一個或多個領域中查找其中的許多內容。

    從這個意義上説,Realm 本質上是一個特定於安全性的 DAO:它封裝了數據源的連接詳細信息,並使相關數據根據需要可供 Shiro 使用。配置 Shiro 時,您必須至少指定一個用於身份驗證和/或授權的 Realm。可以配置多個 Realm,但至少需要一個。

    Shiro 提供開箱即用的 Realms 來連接到許多安全數據源(也稱為目錄),例如 LDAP、關係數據庫 (JDBC)、文本配置源(例如 INI 和屬性文件)等等。如果默認 Realm 不能滿足您的需求,您可以插入自己的 Realm 實現來表示自定義數據源。下面的清單 4 是配置 Shiro(通過 INI)以使用 LDAP 目錄作為應用程序的領域之一的示例。

    清單4:連接到 LDAP 用户數據存儲的領域配置片段示例

    [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 環境,讓我們討論一下作為開發人員如何使用該框架。

六、Authentication

身份驗證是驗證用户身份的過程。也就是説,當用户使用應用程序進行身份驗證時,他們就證明自己確實是他們所説的人。這有時也稱為“登錄”。這通常是一個三步過程。

  1. 收集用户的身份信息(稱為主體)和支持身份證明(稱為憑據)。
  2. 將主體和憑證提交到系統。
  3. 如果提交的憑據與系統對該用户身份(主體)的期望相匹配,則該用户被視為已通過身份驗證。如果不匹配,則用户不會被視為已通過身份驗證。

每個人都熟悉的此過程的一個常見示例是用户名/密碼組合。當大多數用户登錄軟件應用程序時,他們通常會提供用户名(主體)和支持密碼(憑據)。如果系統中存儲的密碼(或其表示形式)與用户指定的內容匹配,則認為他們已通過身份驗證。

Shiro 以簡單直觀的方式支持相同的工作流程。正如我們所説,Shiro 有一個以主題為中心的 API - 幾乎您在運行時使用 Shiro 做的所有事情都是通過與當前執行的主題交互來實現的。因此,要登錄主題,您只需調用其登錄方法,傳遞代表提交的主體和憑據(在本例中為用户名和密碼)的 AuthenticationToken 實例。下面的清單 5 顯示了這個示例。

清單5:主題登錄

//1. 獲取提交的主體和憑證:
AuthenticationToken token =
new UsernamePasswordToken(username, password);

//2. 獲取當前主題:
Subject currentUser = SecurityUtils.getSubject();
//3. 登錄:
currentUser.login(token);

正如您所看到的,Shiro 的 API 很容易反映常見的工作流程。您將繼續將這種簡單性視為所有主題操作的主題。當調用登錄方法時,安全管理器將接收身份驗證令牌並將其分派到一個或多個配置的領域,以允許每個領域根據需要執行身份驗證檢查。每個領域都能夠根據需要對提交的 AuthenticationToken 做出反應。但是如果登錄嘗試失敗會發生什麼?如果用户指定了錯誤的密碼怎麼辦?您可以通過對 Shiro 的運行時 AuthenticationException 做出反應來處理故障,如清單 6 所示。

清單6:處理登錄失敗

try {
    currentUser.login(token);
} catch (IncorrectCredentialsException ice) { …
} catch (LockedAccountException lae) { …
}
…
catch (AuthenticationException ae) {…
}

您可以選擇捕獲 AuthenticationException 子類之一併做出具體反應,或者一般性地處理任何 AuthenticationException(例如,向用户顯示通用的“用户名或密碼不正確”消息)。您可以根據您的應用要求進行選擇。

主題成功登錄後,他們被視為已通過身份驗證,通常您允許他們使用您的應用程序。但僅僅因為用户證明了他們的身份並不意味着他們可以在您的應用程序中做任何他們想做的事情。這就引出了下一個問題:“我如何控制用户可以做什麼或不可以做什麼?”決定允許用户做什麼稱為授權。接下來我們將介紹 Shiro 如何啓用授權。

七、Authorization

授權本質上是訪問控制——控制用户可以在應用程序中訪問哪些內容,例如資源、網頁等。大多數用户通過使用角色和權限等概念來執行訪問控制。也就是説,通常允許用户執行或不執行某些操作,具體取決於分配給他們的角色和/或權限。然後,您的應用程序可以根據對這些角色和權限的檢查來控制公開哪些功能。正如您所期望的,Subject API 允許您非常輕鬆地執行角色和權限檢查。例如,清單 7 中的代碼片段顯示瞭如何檢查主題是否已被分配特定角色。

清單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”權限的角色或用户都可以單擊“創建用户”按鈕,並且這些角色和分配甚至可以在運行時更改,從而為您提供非常靈活的安全模型。

“user:create”字符串是遵循某些解析約定的權限字符串的示例。Shiro 通過其 WildcardPermission 開箱即用地支持此約定。儘管超出了本文的介紹範圍,但您將看到 WildcardPermission 在創建安全策略時非常靈活,甚至支持實例級訪問控制等功能。

清單9:實例級權限檢查

if ( subject.isPermitted(“user:delete:jsmith”) ) {
    //delete the ‘jsmith’ user
} else {
    //don’t delete ‘jsmith’
}

此示例表明,如果需要,您可以控制對各個資源的訪問,甚至可以控制到非常細粒度的實例級別。如果您願意,您甚至可以發明自己的權限語法。有關更多信息,請參閲 Shiro 權限文檔。最後,就像身份驗證一樣,上述調用最終會到達 SecurityManager,SecurityManager 將諮詢一個或多個領域來做出訪問控制決策。這允許領域根據需要響應身份驗證和授權操作。

這就是 Shiro 授權功能的簡要概述。雖然大多數安全框架僅限於身份驗證和授權,但 Shiro 提供了更多功能。接下來我們將討論 Shiro 的高級會話管理功能。

八、Session Management

Apache Shiro提供了世界上獨一無二的安全框架:一個可用於任何應用程序和任何架構層的一致的會話API。也就是説,Shiro 為任何應用程序啓用了會話編程範例 - 從小型守護程序獨立應用程序到最大的集羣 Web 應用程序。這意味着希望使用會話的應用程序開發人員不再被迫使用 Servlet 或 EJB 容器(如果他們不需要)。或者,如果使用這些容器,開發人員現在可以選擇在任何層中使用統一且一致的會話 API,而不是 servlet 或 EJB 特定的機制。

但也許 Shiro 會話最重要的好處之一是它們獨立於容器。這具有微妙但極其強大的含義。例如,讓我們考慮會話集羣。有多少種特定於容器的方法來集羣會話以實現容錯和故障轉移? Tomcat 的做法與 Jetty 不同,Jetty 的做法又與 Websphere 等不同。但通過 Shiro 會話,您可以獲得獨立於容器的集羣解決方案。 Shiro 的架構允許可插入的會話數據存儲,例如企業緩存、關係數據庫、NoSQL 系統等。這意味着您只需配置一次會話集羣,無論您的部署環境如何(Tomcat、Jetty、JEE Server 或獨立應用程序),它都會以相同的方式工作。無需根據您部署應用程序的方式重新配置您的應用程序。Shiro 會話的另一個好處是,如果需要,可以跨客户端技術共享會話數據。例如,如果需要,Swing 桌面客户端可以參與同一個 Web 應用程序會話 - 如果最終用户同時使用兩者,這很有用。那麼如何在任何環境中訪問主題的會話呢?有兩種主題方法,如下例所示。

清單10:主題會話

Session session = subject.getSession();
Session session = subject.getSession(boolean create);

正如您所看到的,這些方法在概念上與 HttpServletRequest API 相同。第一個方法將返回主題的現有會話,或者如果還沒有會話,它將創建一個新會話並返回它。第二種方法接受一個布爾參數,用於確定是否創建新會話(如果尚不存在)。一旦獲得了主題的會話,就可以像使用 HttpSession 一樣使用它。 Shiro 團隊認為 HttpSession API 對於 Java 開發人員來説是最舒服的,因此我們保留了它的大部分感覺。當然,最大的區別是您可以在任何應用程序中使用 Shiro Sessions,而不僅僅是 Web 應用程序。清單 11 顯示了這種熟悉性。

清單11:會話方法

Session session = subject.getSession();
session.getAttribute(“key”, someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);

九、Cryptography

密碼學是隱藏或混淆數據的過程,使窺探者無法理解它。 Shiro 在密碼學方面的目標是簡化並提供 JDK 的密碼學支持。

值得注意的是,密碼學通常並不特定於主題,因此它是 Shiro API 中不特定於主題的一個領域。您可以在任何地方使用 Shiro 的加密支持,即使沒有使用主題。Shiro 真正關注加密支持的兩個領域是加密哈希(也稱為消息摘要)和加密密碼。讓我們更詳細地看看這兩個。

  • 哈希

    如果您使用過 JDK 的 MessageDigest 類,您很快就會意識到它使用起來有點麻煩。它有一個笨拙的基於靜態方法工廠的 API,而不是面向對象的 API,並且您被迫捕獲可能永遠不需要捕獲的已檢查異常。如果您需要對消息摘要輸出進行十六進制編碼或 Base64 編碼,則只能靠您自己了——兩者都沒有標準的 JDK 支持。 Shiro 通過乾淨直觀的哈希 API 解決了這些問題。

    例如,讓我們考慮一下相對常見的情況,即對文件進行 MD5 哈希處理並確定該哈希值的十六進制值。這稱為“校驗和”,在提供文件下載時經常使用 - 用户可以對下載的文件執行自己的 MD5 哈希,並斷言其校驗和與下載網站上的校驗和匹配。如果它們匹配,用户就可以充分假設文件在傳輸過程中沒有被篡改。

    以下是您在沒有 Shiro 的情況下嘗試執行此操作的方法:

    1. 將文件轉換為字節數組。 JDK 中沒有任何東西可以幫助解決這個問題,因此您需要創建一個輔助方法來打開 FileInputStream、使用字節緩衝區並拋出適當的 IOException 等。
    2. 使用 MessageDigest 類對字節數組進行哈希處理,處理適當的異常,如下面的清單 12 所示。
    3. 將哈希字節數組編碼為十六進制字符。 JDK 中也沒有任何東西可以幫助解決這個問題,因此您需要創建另一個輔助方法,並且可能在實現中使用按位運算和位移。

    清單12:JDK 的 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 Cryptography 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 所示。

    清單13:Apache Shiro 加密 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 Support

最後但並非最不重要的一點是,我們將簡要介紹 Shiro 的 Web 支持。 Shiro 附帶強大的 Web 支持模塊,以幫助保護 Web 應用程序的安全。為 Web 應用程序設置 Shiro 非常簡單。唯一需要做的就是在 web.xml 中定義 Shiro Servlet Filter。清單 14 顯示了此代碼。

清單14:web.xml 中的 ShiroFilter

<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 Filter 將過濾每個請求,並確保請求特定的主題在請求期間可訪問。由於它會過濾每個請求,因此您可以執行特定於安全性的邏輯,以確保只允許滿足特定條件的請求通過。

  • URL特定的過濾器鏈

    Shiro 通過其創新的 URL 過濾器鏈接功能支持特定於安全的過濾規則。它允許您為任何匹配的 URL 模式指定臨時過濾器鏈。這意味着您在使用 Shiro 的過濾機制執行安全規則(或規則組合)時具有很大的靈活性 - 比單獨在 web.xml 中定義過濾器要靈活得多。清單 15 顯示了 Shiro INI 中的配置片段。

    清單15:特定於路徑的過濾器鏈

    [urls]
    /assets/** = anon
    /user/signup = anon
    /user/** = user
    /rpc/rest/** = perms[rpc:invoke], authc
    /** = authc

    正如您所看到的,有一個 [urls] INI 部分可供 Web 應用程序使用。對於每一行,等號左側的值表示上下文相關的 Web 應用程序路徑。右側的值定義了一個過濾器鏈 - 一個有序的、以逗號分隔的 Servlet 過濾器列表,要針對給定路徑執行。每個過濾器都是普通的 Servlet 過濾器,但您在上面看到的過濾器名稱(anon、user、perms、authc)是 Shiro 開箱即用提供的特殊安全相關過濾器。您可以混合搭配這些安全過濾器來創建非常自定義的安全體驗。您還可以指定任何其他現有的 Servlet 過濾器。

    與使用 web.xml 相比,這要好得多,在 web.xml 中定義一個過濾器塊,然後定義一個單獨的斷開連接的過濾器模式塊?使用 Shiro 的方法,可以更輕鬆地準確查看針對給定匹配路徑執行的過濾器鏈。如果您願意,您可以在 web.xml 中僅定義 Shiro 過濾器,並在 shiro.ini 中定義所有其他過濾器和過濾器鏈,以獲得比 web.xml 更簡潔且易於理解的過濾器鏈定義機制。即使您沒有使用 Shiro 的任何安全功能,僅這一點小小的便利就足以讓 Shiro 值得使用。

  • JSP標籤庫

    Shiro 還提供了一個 JSP 標籤庫,允許您根據當前主題的狀態控制 JSP 頁面的輸出。一個常見的例子是在用户登錄後顯示“Hello <username class="TnITTtw-t">”文本。但如果他們是匿名的,您可能想顯示其他內容,例如“您好!今天就註冊!”反而。清單 16 顯示瞭如何使用 Shiro 的 JSP 標記來支持這一點。

    清單16:JSP 標籤庫示例

    <%@ 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 的功能,例如簡單的“記住我”服務、REST 和 BASIC 身份驗證,當然還有透明的 HttpSession 支持(如果您想使用 Shiro 的本機企業會話)。請參閲 Apache Shiro Web 文檔瞭解更多信息。

  • 網絡會話管理

    最後,有趣的是 Shiro 對 Web 環境中會話的支持。

    • 默認 Http 會話

      對於 Web 應用程序,Shiro 默認其會話基礎結構使用我們都習慣的現有 Servlet 容器會話。也就是説,當您調用方法 subject.getSession() 和 subject.getSession(boolean) 時,Shiro 將返回由 Servlet 容器的 HttpSession 實例支持的 Session 實例。這種方法的優點在於,調用 subject.getSession() 的業務層代碼與 Shiro Session 實例進行交互 - 它不“知道”它正在使用基於 Web 的 HttpSession 對象。當保持跨架構層的清晰分離時,這是一件非常好的事情。

    • Shiro 在 Web 層的本機會話

      如果您在 Web 應用程序中啓用了 Shiro 的本機會話管理,因為您需要 Shiro 的企業會話功能(例如獨立於容器的集羣),那麼您當然希望 HttpServletRequest.getSession() 和 HttpSession API 能夠與“本機”會話一起工作,並且不是 servlet 容器會話。如果您必須重構任何使用 HttpServletRequest 和 HttpSession API 的代碼來代替使用 Shiro 的 Session API,那將是非常令人沮喪的。白當然不會想到你會這麼做。相反,Shiro 完全實現了 Servlet 規範的 Session 部分,以支持 Web 應用程序中的本機會話。這意味着每當您調用相應的 HttpServletRequest 或 HttpSession 方法調用時,Shiro 都會將這些調用委託給其內部本機 Session API。最終結果是,即使您使用 Shiro 的“本機”企業會話管理,您也不必更改 Web 代碼 - 這確實是一個非常方便(且必要)的功能。

十一、附加功能

Apache Shiro 框架中還有其他功能對於保護 Java 應用程序很有用,例如:

  • 用於跨線程維護主題的線程和併發支持(Executor 和 ExecutorService 支持)
  • Callable 和 Runnable 支持作為特定主題執行邏輯
  • “運行方式”支持假設另一個主體的身份(例如在管理應用程序中有用)
  • 測試工具支持,使得在單元測試和集成測試中對 Shiro 安全代碼進行全面測試變得非常容易

十二、框架限制

儘管我們希望如此,Apache Shiro 並不是“靈丹妙藥”——它無法輕鬆解決所有安全問題。 Shiro 沒有提及的一些事情可能值得了解:

  • 虛擬機級別的問題:Apache Shiro 目前不處理虛擬機級別的安全性,例如基於訪問控制策略阻止某些類加載到類加載器中的能力。然而,Shiro 可以與現有的 JVM 安全操作集成並不是不可想象的——只是沒有人為該項目貢獻這樣的工作。
  • 多階段身份驗證:Shiro 目前本身不支持“多階段”身份驗證,即用户可能通過一種機制登錄,然後被要求使用不同的機制再次登錄。這已經在基於 Shiro 的應用程序中完成,但是應用程序預先收集所有必需的信息,然後與 Shiro 交互。未來的 Shiro 版本很可能會支持此功能。
  • Realm 寫入操作:目前所有 Realm 實現都支持“讀取”操作,用於獲取身份驗證和授權數據以執行登錄和訪問控制。不支持“寫入”操作,例如創建用户帳户、組和角色,或將用户與角色組和權限關聯。這是因為支持這些操作的數據模型在不同的應用程序中差異很大,並且很難對所有 Shiro 用户強制執行“寫入”API。

十三、即將推出的功能

Apache Shiro 社區每天都在不斷髮展,Shiro 的功能也隨之不斷髮展。在即將推出的版本中,您可能會看到:

  • 更乾淨的 Web 過濾器機制,允許更多可插入過濾支持,而無需子類化。
  • 更多可插入的默認 Realm 實現有利於組合而不是繼承。您將能夠插入查找身份驗證和授權數據的組件,而不需要子類化 Shiro Realm 實現
  • 強大的 OpenID 和 OAuth(可能還有混合)客户端支持
  • 驗證碼支持
  • 更輕鬆地配置 100% 無狀態應用程序(例如許多 REST 環境)。
  • 通過請求/響應協議進行多階段身份驗證。
  • 通過 AuthorizationRequest 進行粗粒度授權。
  • 用於安全斷言查詢的 ANTLR 語法(例如 (‘role(admin) && (guest || !group(developer))’)

十四、總結

Apache Shiro 是一個功能齊全、強大且通用的 Java 安全框架,可用於保護您的應用程序。通過簡化應用程序安全的四個領域,即身份驗證、授權、會話管理和密碼學,應用程序安全性在實際應用程序中更容易理解和實現。 Shiro 的簡單架構和 JavaBeans 兼容性使其幾乎可以在任何環境中配置和使用。額外的 Web 支持和輔助功能(如多線程和測試支持)完善了框架,為您的應用程序安全提供了“一站式服務”。 Apache Shiro 的開發團隊不斷前進,完善代碼庫並支持社區。隨着開源和商業應用的持續發展,Shiro 有望變得更加強大。

十五、資源

  • Apache Shiro的主頁
  • Shiro 的下載頁面,包含 Maven 和 Ant+Ivy 用户的附加信息。
  • Apache Shiro 的文檔頁面,包含指南和參考手冊
  • Apache Shiro 演示視頻和幻燈片,由該項目的 PMC 主席 Les Hazlewood 演示。
  • 其他 Apache Shiro 文章和演示文稿
  • Apache Shiro 郵件列表和論壇。
user avatar lankerens 頭像 an_653b347d1d3da 頭像 mokeywie 頭像 13917911249 頭像 yadong_zhang 頭像 mo_or 頭像 deltaf 頭像
7 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.