Apache Shiro 淺析

以前在工做中有比較快速的學習過Shiro安全框架,如今因爲新項目須要,深刻研究和學習Shiro的一些知識,填補安全管理方面的知識欠缺,使咱們在web 開發領域更具競爭力,不作只會CRUD的程序員!html

 

一.Shiro簡介

Shiro是一個Java安全框架,執行身份驗證、受權、密碼、會話管理。Shiro是Apache 的一個開源項目,前身是JSecurity 項目,始於2003年初。程序員

Shiro 能夠爲任何應用提供安全保障 - 從命令行應用、移動應用到大型網絡及企業應用。web

shiro 解決了應用安全的四要素:算法

  • 認證 - 用戶身份識別,常被稱爲用戶「登陸」;
  • 受權 - 訪問控制;
  • 密碼加密 - 保護或隱藏數據防止被偷窺;
  • 會話管理 - 每用戶相關的時間敏感的狀態。

 同時,Shiro另外支持了一些輔助特性:如 Web 應用安全、單元測試和多線程,它們的存在強化了上面提到的四個要素。數據庫

 

二.Shiro的優點

從 2003 年至今,框架選擇方面的狀況已經改變了很多,但今天仍有使人信服的理由讓你選擇 Shiro。其實理由至關多,Apache Shiro:apache

  一、易於使用 - 易用性是這個項目的最終目標。應用安全有可能會很是讓人糊塗,使人沮喪。如果能讓它簡化到新手都能很快上手,那它將再也不是一種痛苦了。編程

  二、普遍性 - 沒有其餘安全框架能夠達到 Apache Shiro 宣稱的廣度,它能夠爲你的安全需求提供「一站式」服務。設計模式

  三、靈活性 - Apache Shiro 能夠工做在任何應用環境中。雖然它工做在 Web、EJB 和 IoC 環境中,但它並不依賴這些環境。Shiro 既不強加任何規範,也無需過多依賴。api

  四、Web 能力 - Apache Shiro 對 Web 應用的支持很神奇,容許你基於應用 URL 和 Web 協議(如 REST)建立靈活的安全策略,同時還提供了一套控制頁面輸出的 JSP 標籤庫。數組

  五、可插拔 - Shiro 乾淨的 API 和設計模式使它能夠方便地與許多的其餘框架和應用進行集成。你將看到 Shiro 能夠與諸如 Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin 這類第三方框架無縫集成。

  六、支持 - Apache Shiro 是 Apache 軟件基金會成員,這是一個公認爲了社區利益最大化而行動的組織。項目開發和用戶組都有隨時願意提供幫助的友善成員。

 

三.核心概念

Shiro的核心概念有三個:Subject,SecurityManager 和 Realms。

3.一、Subject

subject 被Shiro 描述爲一個主體,對於web應用來講,能夠簡單理解爲用戶。

這裏咱們來闡述一個Shiro設計的重要理念,即以主體爲展開的安全體系構建。引用一段話:

在考慮應用安全時,你最常問的問題多是「當前用戶是誰?」或「當前用戶容許作 X 嗎?」。當咱們寫代碼或設計用戶界面時,問本身這些問題很日常:應用一般都是基於用戶故事構建的,而且你但願功能描述(和安全)是基於每一個用戶的。因此,對於咱們而言,考慮應用安全的最天然方式就是基於當前用戶。Shiro 的 API 用它的 Subject 概念從根本上體現了這種思考方式。

在應用程序中,咱們能夠在任何地方獲取當前操做的用戶主體:

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

得到Subject 後,經過這個對象,咱們能夠對其進行絕大多數安全操做:登陸、登出、訪問會話、執行受權檢查等。

Shiro 的api很是直觀,它反映了開發者以「每一個用戶」 思考安全控制的天然思惟方式。

3.二、SecurityManager

Subject 的幕後推手是 SecurityManager,Subject 表明了當前用戶的安全操做,SecurityManager則管理全部用戶的安全操做。

SecurityManager 是 Shiro 框架的核心,充當「保護傘」,引用了多個內部嵌套安全組件,它們造成了對象圖。可是,一旦 SecurityManager 及其內部對象圖配置好,它就會退居幕後,應用開發人員幾乎把他們的全部時間都花在 Subject API 調用上。

一個應用只須要一個 SecurityManager,是一個單例對象。它的缺省實現是POJO,Shiro 裏的其餘組件也是同樣。所以,能夠用POJO兼容的任何配置機制進行配置:普通的Java代碼、Spring xml、YAML、和 ini 文件等。基本上,可以實例化類和調用JavaBean兼容方法的任何配置形式均可以。

好比,經過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

[main] 段落是配置SecurityManager 對象及其使用的其餘任何對象(如 Realm) 的地方,在上面的示例中,咱們看到了兩個對象:

  一、cm對象,是Shiro 的HashedCredentialsMatcher 類實例,cm 的各屬性經過「嵌套點」語法進行配置。

  二、iniRealm對象,被 SecurityManager 用來表示以INI 格式定義的用戶帳戶。

而後,咱們在Java代碼中,能夠垂手可得的得到 SecurityManager對象了:

//1. 加載 INI 配置 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2. 建立 SecurityManager SecurityManager securityManager = factory.getInstance(); //3. 使其可訪問 SecurityUtils.setSecurityManager(securityManager);

3.三、Realm

Realm 充當了 Shiro 與應用安全數據間的「橋樑」或者「鏈接器」。當切實與像用戶賬戶這類安全相關數據進行交互,執行認證(登陸)和受權(訪問控制)時,Shiro 會從應用配置的 Realm 中查找不少內容。

從某種意義上講,Realm 實際上就是一個安全相關的 DAO:它封裝了數據源的鏈接細節,並在須要時將相關數據提供給 Shiro。

注意:在配置Shiro 時,必須指定至少一個 Realm ,能夠配置多個。

Shiro 內置了一些Realm ,支持多種數據源的鏈接,如JDBC、LDAP、INI文件的鏈接等。另外,能夠自定義Realm 實現,方便個性化的應用場景。

 

四.典型的應用場景概述

應用安全的四要素:認證、受權、會話管理、加密。

4.1 認證

雖然有些武斷,可是通常web 應用認證就是登陸功能。也就是說,當用戶使用應用進行認證時,他們就在證實他們就是本身所說的那我的。

這是一個典型的三步過程:

  一、收集用戶身份信息,成爲當事人(principal),以及身份的支持證實,稱爲證書(Credential)。

  二、將當事人和證書提交給系統。

  三、若是提交的證書與系統指望的該用戶身份(當事人)匹配,該用戶就被認爲是通過認證的,反之則被認爲未經認證的。

Shiro 以簡單直觀的方式支持一樣的流程。Shiro 有一套以Subject 爲中心的API,幾乎你想要用 Shiro 在運行時完成的全部事情都能經過與當前執行的 Subject 進行交互而達成。所以,要登陸 Subject,只須要簡單地調用它的 login 方法。傳入表示被提交當事人和證書(在這種狀況下,就是用戶名和密碼)的 AuthenticationToken 實例。

//1. 接受提交的當事人和證書: AuthenticationToken token = new UsernamePasswordToken(username, password); //2. 獲取當前 Subject: Subject currentUser = SecurityUtils.getSubject(); //3. 登陸: currentUser.login(token);

能夠看到,Shiro的操做極其簡潔和天然,這也是Shiro 慣有的風格。在調用 login()方法後,SecurityManager 會收到AuthenticationToken,並將其發送給已配置的 Realm,執行必須的認證檢查,以往咱們手動去數據庫中進行校驗和匹配的時代已通過去了,這些全部的操做,所有由Shiro 幫咱們自動完成。當數據通過Realm 的檢查後發現沒法匹配,那麼Shiro 就會返回 AuthenticationException 異常的子類,經過這些子類,咱們能夠精確的控制想要返回給用戶的錯誤信息:

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

若是沒有拋出任何異常,則證實 Subject 登陸成功,就被認爲是已認證的。

4.2 受權

受權實質上就是訪問控制,控制已認證的用戶可以訪問應用的哪些內容,如資源、頁面等。

多數用戶執行訪問控制是經過 角色 + 權限 的概念來完成的。角色是全部用戶個體的一個分組,如管理員、普通用戶、商家等;而權限 則表示具體可以操做的行爲,好比查詢全部用戶、刪除某些用戶、修改信息等等,是與具體應用資源直接掛鉤的。

用戶、角色和 權限三者每每經過 角色 來進行轉換,用戶和權限之間一般不進行直接綁定:

咱們能夠經過shiro的校驗方法,來便捷地實現分支語句:

if ( subject.hasRole("administrator") ) { // 顯示‘Create User’按鈕 } else { // 按鈕置灰 } 

雖然,在概念上,權限與角色直接掛鉤,但其最終效果仍是要落實到具體的某個用戶是否具備某個權限,爲此,Shiro也爲咱們提供了相應的校驗方法:

if ( subject.isPermitted("user:create") ) { // 顯示‘Create User’按鈕 } else { // 按鈕置灰 } 

這樣,任何具備「user:create」權限的角色或用戶均可以點擊‘Create User’按鈕,而且這些角色和指派甚至能夠在運行時改變,這給你提供了一個很是靈活的安全模型。

上例中,"user:create" 字符串是一種遵循特定規則的權限描述符,具體詳情可瞭解:http://shiro.apache.org/permissions.html

上面這些權限的調用,最終都會發送到SecurityManager中,它會諮詢 Realm 作出本身的訪問控制決定。必要時,還容許單個 Realm 同時響應認證和受權操做。

4.3 會話管理

在以往的Servlet應用中,咱們最常使用的會話對象就是 HttpSession 對象。

在Shiro 中,也有屬於本身的會話管理機制和用戶的會話對象。Shiro 容許開發者在任何應用或架構層一致地使用 Session API。

它爲任何應用(從小型後臺獨立應用到大型集羣 Web 應用)提供了一個會話編程範式。這意味着,那些但願使用會話的應用開發者,沒必要被迫使用 Servlet 或 EJB 容器了。或者,若是正在使用這些容器,開發者如今也能夠選擇使用在任何層統一一致的會話 API,取代 Servlet 或 EJB 機制。

Shiro 會話最重要的一個好處或許就是它們是獨立於容器的。這個特性的做用很是巨大,設想一下會話集羣。對集羣會話來說,支持容錯和故障轉移有多少種容器特定的方式?Tomcat 的方式與 Jetty 的不一樣,而 Jetty 又和 Websphere 不同,等等。但經過 Shiro 會話,你能夠得到一個容器無關的集羣解決方案。

Shiro 的架構容許可插拔的會話數據存儲,如企業緩存、關係數據庫、NoSQL 系統等。這意味着,只要配置會話集羣一次,它就會以相同的方式工做,跟部署環境無關 - Tomcat、Jetty、JEE 服務器或者獨立應用。無論如何部署應用,毋須從新配置應用。

獲取當前用戶的Session 對象,咱們可使用下面這樣的方法:

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

上面這些方法在概念上等同於HttpServletRequest API。第一個方法會返回 Subject 的現有會話,或者若是尚未會話,它會建立一個新的並將之返回。第二個方法接受一個布爾參數,這個參數用於斷定會話不存在時是否建立新會話。一旦得到 Shiro 的會話,你幾乎能夠像使用 HttpSession 同樣使用它。Shiro 保留的 HttpSession 的使用體驗,但不一樣的是 Shiro 能夠在任何應用中使用會話機制,不只限於Web應用。 Shiro  Session 的一些方法:

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

4.4 加密

在加密方面,Shiro 儘量的簡化加密處理的步驟,並讓JDK的加密支持可用。

注意一點,加密並非特定於Subject 的,加密的特性是 Shiro 的一部分,但不特定於僅僅對 Subject 的處理。咱們能夠在任何地方使用 Shiro 的加密支持,甚至在不使用 Subject 的狀況下。

對於加密支持,Shiro 真正關心的兩個領域是加密哈希(又名消息摘要)和加密密碼。

傳統的JDK加密操做過於複雜,並且是基於笨拙的工廠靜態方法api,它並非面向對象的設計:

ry {
    MessageDigest md = MessageDigest.getInstance("MD5"); md.digest(bytes); byte[] hashed = md.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } 

若是在某些場景下頻繁會用到哈希加密的狀況,那麼開發者就必需要封裝以後才能比較輕鬆的使用JDK 的加密支持,不然將會是一個不小的工做量。Shiro 爲咱們提供了這樣的便捷,對於相似的功能,只須要簡單的幾行代碼就能夠完成,同時也省去了一些老舊的和沒必要要的異常捕獲:

// 面向對象的MD5加密方式 String hex = new Md5Hash(myFile).toHex(); // SHA-512 String encodedPassword = new Sha512Hash(password, salt, count).toBase64();

剛剛提到的這些是一些必要的哈希算法API,另外一個Shiro 關心的事情就是 加密密碼

咱們知道,加密是使用密鑰對數據進行可逆轉換的加密算法。咱們使用其保證數據的安全,尤爲是傳輸或存儲數據時,以及在數據容易被窺探的時候。

一樣 JDK 的加密API也很是複雜和難用,而Shiro 經過引入它的 CipherService API 試圖簡化加密密碼的整個概念。

CipherService 是多數開發者在保護數據時求之不得的東西:簡單、無狀態、線程安全的 API,可以在一次方法調用中對整個數據進行加密或解密。你所須要作的只是提供你的密鑰,就可根據須要加密或解密。
AesCipherService cipherService = new AesCipherService(); cipherService.setKeySize(256); // 建立一個測試密鑰: byte[] testKey = cipherService.generateNewKey(); // 加密文件的字節: byte[] encrypted = cipherService.encrypt(fileBytes, testKey);

較之 JDK 的 Cipher API,Shiro 的示例要簡單的多:

  • 你能夠直接實例化一個 CipherService - 沒有奇怪或讓人混亂的工廠方法;
  • Cipher 配置選項能夠表示成 JavaBean - 兼容的 getter 和 setter 方法 - 沒有了奇怪和難以理解的「轉換字符串」;
  • 加密和解密在單個方法調用中完成;
  • 沒有強加的 Checked Exception。若是願意,能夠捕獲 Shiro 的 CryptoException。

Shiro 的 CipherService API 還有其餘好處,如同時支持基於字節數組的加密 / 解密(稱爲「塊」操做)和基於流的加密 / 解密(如加密音頻或視頻)。

 

五.Web支持

Shiro 附帶了一個幫助保護web應用的強健的web 支持模塊,除了配置基本的依賴和Shiro 的核心單例組件 - SecurityManager 以外,可能剩下的就只是要配置一個Shiro Servlet 過濾器了。

當咱們完成了 這些前期的必要的配置工做後,Shiro Filter 就會過濾每一個請求,並確保在請求期間特定的 Subject 是可訪問的。同時因爲它過濾了每一個請求,你能夠執行安全特定的邏輯以保證只有知足必定標準的請求才被容許經過。

5.1 URL 過濾器

Shiro 經過其創新的 URL 過濾器鏈功能支持安全特定的過濾規則。

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

對於每一行,等號左邊的值表示相對上下文的 Web 應用路徑。等號右邊的值定義了過濾器鏈 - 一個逗號分隔的有序 Servlet 過濾器列表,它會針對給出的路徑進行執行。每一個過濾器都是普通的 Servlet 過濾器,你看到的上面的過濾器名字(anon,user,perms,authc)是 Shiro 內置的安全相關的特殊過濾器。你能夠搭配這些安全過濾器來建立高度定製的安全體驗。你還能夠指定任何其餘現有的 Servlet 過濾器。

5.2 Web 會話管理

對於 Web 應用,Shiro 缺省將使用咱們習覺得常的 Servlet 容器會話做爲其會話基礎設施。即,當調用 subject.getSession() 和 subject.getSession(boolean) 方法時,Shiro 會返回 Servlet 容器的 HttpSession 實例支持的 Session 實例。

這種方式的曼妙之處在於調用 subject.getSession() 的業務層代碼會跟一個 Shiro Session 實例交互 - 尚未「認識」到它正跟一個基於 Web 的 HttpSession 打交道。這在維護架構層之間的清晰隔離時,是一件很是好的事情。

另外,當須要使用與容器無關的會話特性時,咱們也能夠開啓Shiro 的原生會話管理。不一樣於傳統的HttpServletRequest.getSession()和 HttpSession API只能和Servlet 容器打交道,Shiro的原生會話管理依然可讓用戶使用相同的 HTTPServletRequest 和 HttpSession 調用完成與原生會話的協做,而不須要重構這些代碼。這是由於 Shiro 完整實現了 Servlet規範中的 Session 部分以在 Web 應用中支持原生會話,所以開發者的全部 HTTPSession 的調用都會被委託給 Shiro 內部的原生會話 API。

 

六.附加特性

Apache Shiro 框架還包含有對保護 Java 應用很是有用的其餘特性,如:

  • 爲維持跨線程的 Suject 提供了線程和併發支持(支持 Executor 和 ExecutorService);
  • 爲了將執行邏輯做爲一種特殊的 Subject,支持 Callable 和 Runnable 接口;
  • 爲了表現爲另外一個 Subject 的身份,支持「Run As」(好比,在管理應用中有用);
  • 支持測試工具,這樣能夠很容易的對 Shiro 的安全代碼進行單元測試和集成測試。

七.總結

Apache Shiro 是一個功能齊全、健壯、通用的 Java 安全框架,你能夠用其爲你的應用護航。經過簡化應用安全的四個領域,即認證、受權、會話管理和加密,在真實應用中,應用安全能更容易被理解和實現。Shiro 的簡單架構和兼容 JavaBean 使其幾乎可以在任何環境下配置和使用。附加的 Web 支持和輔助功能,好比多線程和測試支持,讓這個框架爲應用安全提供了「一站式」服務。

相關文章
相關標籤/搜索