Apache Shiro™是一個功能強大且易於使用的Java安全框架,可執行身份驗證,受權,加密和會話管理。藉助Shiro易於理解的API,能夠快速輕鬆地保護任何應用程序 - 從最小的移動應用程序到最大的Web和企業應用程序。java
shiro經過如下爲平臺的應用程序的提供安全API。web
shiro提供一些輔助功能,如:Web應用程序安全、單元測試、多線程支持,但都圍繞這四基石爲準。數據庫
身份驗證是驗證用戶身份的過程。也就是說,當用戶使用應用程序進行身份驗證時,他們證實他們其實是他們所說的人。這有時也被稱爲「登陸」。這一般是一個三步過程。apache
每一個人都熟悉的這個過程的一個常見例子是用戶名/密碼組合。當大多數用戶登陸軟件應用程序時,他們一般會提供用戶名(主體)和支持密碼(憑證)。若是存儲在系統中的密碼(或其表示)與用戶指定的密碼匹配,則認爲它們已通過身份驗證。編程
Shiro以簡單直觀的方式支持相同的工做流程。正如咱們所說,Shiro有一個以主題爲中心的API - 幾乎全部你在運行時用Shiro作的事都是經過與當前正在執行的Subject進行交互來實現的。所以,要登陸主題,只需調用其登陸方法,傳遞一個AuthenticationToken實例,該實例表示提交的主體和憑據(在本例中爲用戶名和密碼)。數組
//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很容易反映出常見的工做流程。您將繼續將此簡單性視爲全部主題操做的主題。調用login方法時,SecurityManager將接收AuthenticationToken並將其分派給一個或多個已配置的域,以容許每一個域根據須要執行身份驗證檢查。每一個Realm都可以根據須要對提交的AuthenticationTokens作出反應。可是若是登陸嘗試失敗會發生什麼?若是用戶指定了錯誤的密碼該怎麼辦?您能夠經過對Shiro的運行時AuthenticationException做出反應來處理故障.緩存
//3. Login:
try {
currentUser.login(token);
} catch (IncorrectCredentialsException ice) { …
} catch (LockedAccountException lae) { …
}
…
catch (AuthenticationException ae) {…
}
複製代碼
能夠選擇捕獲其中一個AuthenticationException子類並進行具體反應,或者一般處理任何AuthenticationException(例如,向用戶顯示通用的「不正確的用戶名或密碼」消息)。根據應用要求,您能夠選擇。安全
在成功登陸主題後,它們將被視爲已經過身份驗證,一般您容許它們使用您的應用程序。但僅僅由於用戶證實了他們的身份並不意味着他們能夠在您的應用程序中作任何他們想作的事情。這引出了下一個問題,「我如何控制容許用戶作什麼?」決定容許用戶作什麼稱爲受權。咱們將介紹Shiro如何啓用受權。網絡
受權本質上是訪問控制 - 控制用戶能夠在應用程序中訪問的內容,例如資源,網頁等。大多數用戶經過使用角色和權限等概念來執行訪問控制。也就是說,一般容許用戶基於分配給他們的角色和/或許可來作某事或不作某事。而後,您的應用程序能夠根據對這些角色和權限的檢查來控制公開的功能。正如您所料,Subject API容許您很是輕鬆地執行角色和權限檢查。例如,清單7中的代碼片斷顯示瞭如何檢查Subject是否已分配了某個角色。session
if ( subject.hasRole(「administrator」) ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
複製代碼
權限檢查是執行受權的另外一種方式。如上例所示檢查角色會遇到一個重大缺陷:您沒法在運行時添加或刪除角色。代碼使用角色名稱進行了硬編碼,所以若是您更改了角色名稱和/或配置,您的代碼就會被破壞!若是須要可以在運行時更改角色的含義,或者根據須要添加或刪除角色,則必須依賴其餘內容。
if ( subject.isPermitted(「user:create」) ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
複製代碼
經過擁有反映應用程序原始功能的權限,只須要更改權限更改應用程序的功能時檢查。反過來,您能夠在運行時根據須要爲角色或用戶分配權限。
這樣,任何分配了「user:create」權限的角色或用戶均可以單擊「建立用戶」按鈕,這些角色和分配甚至能夠在運行時更改,提供很是靈活的安全模型。
「user:create」字符串是遵循某些解析約定的權限字符串的示例。Shiro經過WildcardPermission開箱即用,支持這種約定。WildcardPermission在建立安全策略時很是靈活,甚至支持實例級訪問控制等內容。
if ( subject.isPermitted(「user:delete:jsmith」) ) {
//delete the ‘jsmith’ user
} else {
//don’t delete ‘jsmith’
}
複製代碼
Apache Shiro在安全框架領域提供了一些獨特的東西:
一致的會話API,可用於任何應用程序和任何架構層。也就是說,Shiro爲任何應用程序啓用了會話編程範例 - 從小型守護程序獨立應用程序到最大的集羣Web應用程序。這意味着但願使用會話的應用程序開發人員再也不須要使用Servlet或EJB容器,不然就不須要它們。
但也許Shiro會話最重要的好處之一就是它們與容器無關。這具備微妙但極其強大的含義。例如,
Session session = subject.getSession();
Session session = subject.getSession(boolean create);
複製代碼
這些方法在概念上與HttpServletRequest API相同。第一種方法將返回Subject的現有Session,或者若是尚未,它將建立一個新的並返回它。第二種方法接受一個布爾參數,該參數肯定是否將建立新的Session(若是它尚不存在)。得到主題會話後,您幾乎可使用它與HttpSession相同。Shiro團隊認爲HttpSession API對Java開發人員來講最爲溫馨,所以咱們保留了不少感受。固然,最大的區別在於您能夠在任何應用程序中使用Shiro Sessions,而不只僅是Web應用程序。
Session session = subject.getSession();
session.getAttribute(「key」, someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);
...
複製代碼
加密是爲了混淆或隱藏數據,因此,第三方不能窺探到對應數據。Shiro的加密使用了JDK的相關加密。
Cryptography在加密中包含了:
JDK中的MessageDigest
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.digest(bytes);
byte[] hashed = md.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
複製代碼
shiro的hash
String encodedPassword = new Sha512Hash(password,salt,count).toBase64();
複製代碼
與JDK的Cipher API相比,Shiro示例更簡單:
Shiro的CipherService API還有其餘好處,例如支持基於字節數組的加密/解密(稱爲「塊」操做)以及基於流的加密/解密(例如,加密音頻或視頻)的能力。
Java密碼學不須要痛苦。Shiro的加密支持旨在簡化您保護數據安全的工做。
Shiro的架構有三個主要概念 - Subject,SecurityManager和Realms。
Subject 這個詞是一個安全術語,基本上是指「當前正在執行的用戶」。但它不被稱爲「用戶」,由於「用戶」這個詞一般與人類有關。在安全的世界,術語「主題」能夠指一我的,但也有多是進程,守護進程賬戶,或任何相似。它只是意味着「當前與軟件交互的東西」。可是,對於大多數意圖和目的,咱們能夠將其視爲Shiro的「用戶」概念。您能夠在代碼中的任何位置輕鬆獲取Shiro Subject,以下所示。
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
...
Subject currentUser = SecurityUtils.getSubject();
複製代碼
得到 Subject 後,能夠當即訪問當前用戶但願使用Shiro執行的90%全部的操做的信息,例如登陸,註銷,訪問其會話,執行受權檢查等等 。但稍後會詳細介紹。這裏的關鍵點是Shiro的API在很大程度上是直觀的,由於它反映了開發人員在 ‘per-user’ 安全控制中思考的天然傾向。在代碼中的任何位置訪問主題也很容易,容許在須要的任何地方進行安全操做。
Subject 的管理者則爲 SecurityManager 。Subject表示當前用戶的操做模式,而 SecurityManager 管理 全部用戶 的安全操做。SecurityManager 是Shiro的核心,充當一種「傘形」對象,它引用了許多造成對象圖的內部嵌套安全組件。
每一個應用程序幾乎老是有一個SecurityManager實例。它本質上是一個應用程序單例(儘管它不須要是靜態單例)。與Shiro中的幾乎全部內容同樣,默認的SecurityManager實現是POJO,可使用任何兼容POJO的配置機制進行配置 - 普通Java代碼,Spring XML,YAML,.properties和.ini文件等。基本上任何可以實例化的東西可使用類和調用JavaBeans兼容的方法。
[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
複製代碼
從以上配置中,咱們能夠看到使用INI配置Shiro時,有兩個關鍵配置項:[main] 和 [users]
[main]部分用於配置SecurityManager對象和/或SecurityManager使用的任何對象(如Realms)。在此示例中,咱們看到正在配置兩個對象:
在[users]部分指定用戶賬戶的靜態列表 - 方便簡單應用程序或測試時。
// 1。加載INI配置
Factory <SecurityManager> factory =
new IniSecurityManagerFactory(「classpath:shiro.ini」);
// 2。建立SecurityManager
SecurityManager securityManager = factory.getInstance();
// 3。使其可訪問
SecurityUtils.setSecurityManager(securityManager);
複製代碼
Realms充當Shiro與應用程序的「橋樑」或‘鏈接器「,也就是說,當實際與安全相關的數據(如用戶賬戶)進行交互以執行身份驗證(登陸)和受權(訪問控制)時,Shiro會從爲應用程序配置的一個或多個領域中查找許多這些內容。
Realm本質上是一個特定於安全性的DAO:它封裝了數據源的鏈接細節,並根據須要使相關數據可用於Shiro。配置Shiro時,必須至少指定一個Realm用於身份驗證和/或受權。能夠配置多個Realm,但至少須要一個。
Shiro提供了開箱即用的Realms,能夠鏈接到許多安全數據源(也稱爲目錄),如LDAP,關係數據庫(JDBC),文本配置源(如INI和屬性文件等)。若是默認域不符合您的須要,能夠插入本身的Realm實現來表示自定義數據源。
[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
複製代碼
經過配置網絡請求、限制等,以此達到請求攔截,以及過濾請求,並將請求到對應資源。
配置網絡過濾:
<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過濾器將過濾每一個請求,並確保在請求期間能夠訪問特定於請求的主題。而且由於它會過濾每一個請求,您能夠執行特定於安全性的邏輯,以確保只容許知足特定條件的請求。
Shiro經過其創新的URL過濾器連接功能支持特定於安全性的過濾規則。它容許您爲任何匹配的URL模式指定ad-hoc過濾器鏈。這意味着您可使用Shiro的過濾機制在執行安全規則(或規則組合)方面具備很大的靈活性 - 遠遠超過您能夠單獨在web.xml中定義過濾器。
[urls]
/assets/** = anon
/user/signup = anon
/user/** = user
/rpc/rest/** = perms[rpc:invoke], authc
/** = authc
複製代碼
上面看到的過濾器名稱(anon,user,perms,authc)是Shiro提供的特殊安全相關過濾器。能夠混合和匹配這些安全篩選器,以建立很是自定義的安全體驗。還能夠指定您可能擁有的任何其餘現有Servlet過濾器。
對於Web應用程序,Shiro默認其會話基礎結構使用咱們之前習慣的現有Servlet容器會話。也就是說,當你調用方法 subject.getSession()和 subject.getSession(boolean)時,Shiro將返回由Servlet容器的HttpSession實例支持的Session實例。
這種方法的優勢在於調用subject.getSession()的業務層代碼與Shiro Session實例交互 - 它沒有「知識」它正在使用基於Web的HttpSession對象。在跨建築層保持清潔分離時,這是一件很是好的事情。
若是已經在Web應用程序中啓用了Shiro的本機會話管理,由於須要Shiro的企業會話功能(如與容器無關的羣集),固然但願HttpServletRequest.getSession()和HttpSession API與「本機」會話一塊兒使用,不是servlet容器會話。
若是你必須重構任何使用HttpServletRequest和HttpSession API的代碼來改成使用Shiro的Session API,那將是很是使人沮喪的。Shiro固然不會期望你這樣作。相反,Shiro徹底實現了Servlet規範的Session部分,以支持Web應用程序中的本機會話。這意味着不管什麼時候調用相應的HttpServletRequest或HttpSession方法調用,Shiro都會將這些調用委託給其內部本地Session API。
Apache Shiro框架中還有其餘一些對保護Java應用程序有用的功能,例如: