目錄
1、關于Shiro
官網介紹
Shiro特點
Shiro基本功能點
Shiro架構
2、快速入門
2.1、Shiro身份驗證
2.2、Shiro授權
1、關于Shiro
官網介紹
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
Apache Shiro™是一個強大且易用的 Java 安全架構, 能夠用于身份驗證、授權、加密和會話管理。Shiro 擁有易于了解的 API, 您可以快速、輕松地獲得任何應用程式——從最小的移動應用程式到最大的網絡和企業應用程式。
簡而言之,Apache Shiro 是一個強大靈活的開源安全架構,可以完全處理身份驗證、授權、加密和會話管理。
Shiro特點
- 易于使用——易用性是項目的最終目标。應用程式安全非常令人困惑和沮喪, 被認為是 “不可避免的災難”。如果你讓它簡化到新手都可以使用它, 它就将不再是一種痛苦了。
- 全面——沒有其他安全架構的寬度範圍可以同 Apache Shiro 一樣, 它可以成為你的 “一站式” 為您的安全需求提供保障。
- 靈活——Apache Shiro 可以在任何應用程式環境中工作。雖然在網絡工作、EJB 和 IoC 環境中可能并不需要它。但 Shiro 的授權也沒有任何規範, 甚至沒有許多依賴關系。
- Web 支援——Apache Shiro 擁有令人興奮的 web 應用程式支援, 允許您基于應用程式的 url 建立靈活的安全政策和網絡協定 (例如 REST), 同時還提供一組 JSP 庫控制頁面輸出。
- 低耦合——Shiro 幹淨的 API 和設計模式使它容易與許多其他架構和應用程式內建。你會看到 Shiro 無縫地內建 Spring 這樣的架構, 以及 Grails, Wicket, Tapestry, Mule, Apache Camel, Vaadin... 等。
- 被廣泛支援——Apache Shiro 是 Apache 軟體基金會的一部分。項目開發和使用者組都有友好的網民願意幫助。這樣的商業公司如果需要 Katasoft 還提供專業的支援和服務。
Shiro基本功能點
- Authentication:身份認證 / 登入,驗證使用者是不是擁有相應的身份;
- Authorization:授權,即權限驗證,驗證某個已認證的使用者是否擁有某個權限;即判斷使用者是否能做事情,常見的如:驗證某個使用者是否擁有某個角色。或者細粒度的驗證某個使用者對某個資源是否具有某個權限;
- Session Management:會話管理,即使用者登入後就是一次會話,在沒有退出之前,它的所有資訊都在會話中;會話可以是普通 JavaSE 環境的,也可以是如 Web 環境的;
- Cryptography:加密,保護資料的安全性,如密碼加密存儲到資料庫,而不是明文存儲;
- Web Support:Web 支援,可以非常容易的內建到 Web 環境;
- Caching:緩存,比如使用者登入後,其使用者資訊、擁有的角色 / 權限不必每次去查,這樣可以提高效率;
- Concurrency:shiro 支援多線程應用的并發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去;
- Testing:提供測試支援;
- Run As:允許一個使用者假裝為另一個使用者(如果他們允許)的身份進行通路;
- Remember Me:記住我,這個是非常常見的功能,即一次登入後,下次再來的話不用登入了。
記住一點,Shiro 不會去維護使用者、維護權限;這些需要我們自己去設計 / 提供;然後通過相應的接口注入給 Shiro 即可。
Shiro架構
在概念層,Shiro 架構包含三個主要的理念:Subject,SecurityManager 和 Realm。下面的圖展示了這些元件如何互相作用,我們将在下面依次對其進行描述。

- Subject:目前使用者,Subject 可以是一個人,但也可以是第三方服務、守護程序帳戶、時鐘守護任務或者其它–目前和軟體互動的任何事件。
- SecurityManager:管理所有 Subject,SecurityManager 是 Shiro 架構的核心,配合内部安全元件共同組成安全傘。
- Realms:用于進行權限資訊的驗證,我們自己實作。Realm 本質上是一個特定的安全 DAO:它封裝與資料源連接配接的細節,得到 Shiro 所需的相關的資料。在配置 Shiro 的時候,你必須指定至少一個 Realm 來實作認證(authentication)和 / 或授權(authorization)。
我們需要實作 Realms 的 Authentication 和 Authorization。其中 Authentication 是用來驗證使用者身份,Authorization 是授權通路控制,用于對使用者進行的操作授權,證明該使用者是否允許進行目前操作,如通路某個連結,某個資源檔案等。
2、快速入門
2.1、Shiro身份驗證
shiro認證流程圖
示例代碼:
1、建立maven工程,引入相關依賴
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2、建立測試類
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthenticationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
// 在方法開始前添加一個使用者
@Before
public void addUser() {
simpleAccountRealm.addAccount("admin", "111111");
}
@Test
public void testAuthentication() {
// 1.建構SecurityManager環境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2.主體送出認證請求
SecurityUtils.setSecurityManager(defaultSecurityManager); // 設定SecurityManager環境
Subject subject = SecurityUtils.getSubject(); // 擷取目前主體
UsernamePasswordToken token = new UsernamePasswordToken("admin", "111111");
subject.login(token); // 登入
// subject.isAuthenticated()方法傳回一個boolean值,用于判斷使用者是否認證成功
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 輸出true
subject.logout(); // 登出
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 輸出false
}
}
3、運作并檢視結果
運作之後可以看到isAuthenticated先列印true,說明認證成功;第二次列印為false,是因為使用者已登出,認證失敗。
具體的身份驗證流程:
流程如下:
- 首先調用
進行登入,其會自動委托給
Subject.login(token)
,調用之前必須通過
Security Manager
設定;
SecurityUtils.setSecurityManager()
負責真正的身份驗證邏輯;它會委托給
SecurityManager
進行身份驗證;
Authenticator
才是真正的身份驗證者,
Authenticator
中核心的身份認證入口點,此處可以自定義插入自己的實作;
Shiro API
可能會委托給相應的
Authenticator
進行多
AuthenticationStrategy
身份驗證,預設
Realm
會調用
ModularRealmAuthenticator
進行多
AuthenticationStrategy
身份驗證;
Realm
會把相應的
Authenticator
傳入
token
,從
Realm
擷取身份驗證資訊,如果沒有傳回 / 抛出異常表示身份驗證失敗了。此處可以配置多個
Realm
,将按照相應的順序及政策進行通路。
Realm
2.2、Shiro授權
授權,也叫通路控制,即在應用中控制誰能通路哪些資源(如通路頁面/編輯資料/頁面操作等)。在授權中需了解的幾個關鍵對象:主體(Subject)、資源(Resource)、權限(Permission)、角色(Role)。
主體
主體,即通路應用的使用者,在 Shiro 中使用 Subject 代表該使用者。使用者隻有授權後才允許通路相應的資源。
資源
在應用中使用者可以通路的URL,比如通路 JSP 頁面、檢視/編輯某些資料、通路某個業務方法、列印文本等等都是資源。使用者隻要授權後才能通路。
權限
安全政策中的原子授權機關,通過權限我們可以表示在應用中使用者有沒有操作某個資源的權力。即權限表示在應用中使用者能不能通路某個資源,如: 通路使用者清單頁面
檢視/新增/修改/删除使用者資料(即很多時候都是 CRUD(增查改删)式權限控制)列印文檔等。如上可以看出,權限代表了使用者有沒有操作某個資源的權利,即反映在某個資源上的操作允不允許,不反映誰去執行這個操作。是以後續還需要把權限賦予給使用者,即定義哪個使用者允許在某個資源上做什麼操作(權限),Shiro 不會去做這件事情,而是由實作人員提供。
角色
角色代表了操作集合,可以了解為權限的集合,一般情況下我們會賦予使用者角色而不是權限,即這樣使用者可以擁有一組權限,賦予權限時比較友善。典型的如:項目經理、技術總監、CTO、開發工程師等都是角色,不同的角色擁有一組不同的權限。
隐式角色:
即直接通過角色來驗證使用者有沒有操作權限,如在應用中 CTO、技術總監、開發工程師可以使用列印機,假設某天不允許開發工程師使用列印機,此時需要從應用中删除相應代碼;再如在應用中 CTO、技術總監可以檢視使用者、檢視權限;突然有一天不允許技術總監檢視使用者、檢視權限了,需要在相關代碼中把技術總監角色從判斷邏輯中删除掉;即粒度是以角色為機關進行通路控制的,粒度較粗;如果進行修改可能造成多處代碼修改。
顯式角色:
在程式中通過權限控制誰能通路某個資源,角色聚合一組權限集合;這樣假設哪個角色不能通路某個資源,隻需要從角色代表的權限集合中移除即可;無須修改多處代碼;即粒度是以資源/執行個體為機關的;粒度較細。
shiro授權流程圖
示例代碼:
1、引入相關依賴,參考2.1節
2、建立測試類
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthorizationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before // 在方法開始前添加一個使用者,讓它具備admin和user兩個角色
public void addUser() {
simpleAccountRealm.addAccount("test", "111111", "admin", "user");
}
@Test
public void testAuthorization() {
// 1.建構SecurityManager環境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2.主體送出認證請求
SecurityUtils.setSecurityManager(defaultSecurityManager); // 設定SecurityManager環境
Subject subject = SecurityUtils.getSubject(); // 擷取目前主體
UsernamePasswordToken token = new UsernamePasswordToken("test", "111111");
subject.login(token); // 登入
// subject.isAuthenticated()方法傳回一個boolean值,用于判斷使用者是否認證成功
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 輸出true
// 判斷subject是否具有admin和user兩個角色權限,如沒有則會報錯
subject.checkRoles("admin","user");
System.out.println("使用者具有admin權限: "+subject.hasRole("admin"));
System.out.println("使用者具有guest權限: "+subject.hasRole("guest"));
}
}
3、運作并檢視結果
具體的授權流程:
流程如下:
- 首先調用
接口,其會委托給 SecurityManager,而 SecurityManager 接着會委托給 Authorizer;
Subject.isPermitted*/hasRole*
- Authorizer 是真正的授權者,如果我們調用如 isPermitted(“user:view”),其首先會通過 PermissionResolver 把字元串轉換成相應的 Permission 執行個體;
- 在進行授權之前,其會調用相應的 Realm 擷取 Subject 相應的角色/權限用于比對傳入的角色/權限;
- Authorizer 會判斷 Realm 的角色/權限是否和傳入的比對,如果有多個 Realm,會委托給 ModularRealmAuthorizer 進行循環判斷,如果比對如
會傳回 true,否則傳回 false 表示授權失敗。
isPermitted*/hasRole*
ModularRealmAuthorizer 進行多 Realm 比對流程:
- 首先檢查相應的 Realm 是否實作了實作了 Authorizer;
- 如果實作了 Authorizer,那麼接着調用其相應的
接口進行比對;
isPermitted*/hasRole*
- 如果有一個 Realm 比對那麼将傳回 true,否則傳回 false。
如果 Realm 進行授權的話,應該繼承 AuthorizingRealm,其流程是:
- 如果調用
,則直接擷取 AuthorizationInfo.getRoles() 與傳入的角色比較即可;首先如果調用如 isPermitted(“user:view”),首先通過 PermissionResolver 将權限字元串轉換成相應的 Permission 執行個體,預設使用 WildcardPermissionResolver,即轉換為通配符的 WildcardPermission;
hasRole*
- 通過 AuthorizationInfo.getObjectPermissions() 得到 Permission 執行個體集合;通過 AuthorizationInfo.getStringPermissions() 得到字元串集合并通過 PermissionResolver 解析為 Permission 執行個體;然後擷取使用者的角色,并通過 RolePermissionResolver 解析角色對應的權限集合(預設沒有實作,可以自己提供);
- 接着調用 Permission.implies(Permission p) 逐個與傳入的權限比較,如果有比對的則傳回 true,否則 false。