文章目錄
- 一、Shiro 核心功能
- 二、Shiro 架構
- 2.1 三層架構
- 2.2 核心組件(SecurityManager 內部)
- 三、核心流程詳解
- 3.1 認證流程(登錄)
- 流程步驟:
- 認證流程序列圖:
- 3.2 授權流程(權限校驗)
- 流程步驟:
- 授權流程序列圖:
- 3.3 會話管理
- 1. 會話管理核心組件
- 2. 會話管理流程
- 3. 關鍵特性
- 3.4 加密
- 1. 核心加密組件
- 2. 密碼加密與驗證流程
- 3. 關于哈希和鹽值的補充說明
- 3.5 其他核心功能
- 1. 緩存(Caching)
- 2. rememberMe(記住我)
- 3. 并發登錄控制
- 四、Shiro 核心組件詳解
- 4.1 Subject
- 4.2 SecurityManager
- 4.3 Realm
- 五、Shiro 與 Web 集成
- 核心配置(web.xml):
- URL 權限配置(shiro.ini):
- 六、同類型產品分析
- 主流方案對比:Shiro vs Spring Security
一、Shiro 核心功能
Shiro 的核心功能可以概括為 “認證、授權、會話管理、加密”,此外還提供了緩存、RememberMe 等輔助功能。
- 認證(Authentication):驗證用戶身份(“你是誰?”),例如登錄時校驗用戶名和密碼。
- 授權(Authorization):驗證用戶權限(“你能做什么?”),例如判斷用戶是否有權限訪問某個接口或按鈕。
- 會話管理(Session Management):管理用戶會話(即使在非 Web 環境中也能使用),支持會話過期、會話存儲等。
- 加密(Cryptography):提供安全的加密算法(如 MD5、SHA)和密碼哈希功能,保護敏感數據。
- 其他功能:緩存(提升權限校驗性能)、RememberMe(記住登錄狀態)、Web 集成、集群會話等。
二、Shiro 架構
Shiro 采用分層架構,從外部到內部分為 “Subject - SecurityManager - Realms” 三層,每層職責明確,且內部包含多個核心組件協同工作。
2.1 三層架構
- Subject:對外的“用戶”抽象(可以是用戶、進程等),所有操作都通過 Subject 觸發(如登錄、權限校驗)。
- SecurityManager:Shiro 的核心,負責協調內部組件完成安全操作(如認證、授權),是 Shiro 的“大腦”。
- Realms:連接應用與數據源的橋梁,用于獲取用戶信息(用戶名/密碼)和權限信息(角色/權限),Shiro 通過 Realms 驗證身份和權限。
2.2 核心組件(SecurityManager 內部)
SecurityManager 內部包含多個組件,分工處理不同的安全功能:
- Authenticator:負責認證流程(如校驗用戶名密碼),默認實現為
ModularRealmAuthenticator
,支持多 Realm 認證。 - Authorizer:負責授權流程(如校驗用戶是否有某權限),默認實現為
ModularRealmAuthorizer
。 - SessionManager:管理用戶會話,不依賴 Web 容器(如 Tomcat),支持在非 Web 環境(如 Swing 應用)中使用。
- CacheManager:緩存用戶信息、權限數據等,減少重復查詢數據源的開銷(默認支持 EhCache、Redis 等)。
- Cryptography:提供加密工具類(如
SimpleHash
),支持 MD5、SHA 等哈希算法,避免明文存儲密碼。
SecurityManager 內部維護了一個或多個 Realm 實例。 SecurityManager 下管理的認證器、授權器會直接與 Realms 交互,其他組件(如 SessionManager、CacheManager 等)不會直接訪問 Realms。
三、核心流程詳解
3.1 認證流程(登錄)
認證流程是驗證用戶身份的過程,核心是校驗“用戶提供的憑證(如密碼)”與“數據源中的憑證”是否一致。
流程步驟:
- 用戶通過 Subject 提交認證請求(如輸入用戶名密碼)。
- Subject 將請求委托給 SecurityManager。
- SecurityManager 委托 Authenticator 處理認證。
- Authenticator 通過 Realm 從數據源獲取用戶信息(如數據庫中的密碼)。
- Authenticator 比對用戶提交的憑證與數據源中的憑證,返回認證結果。
認證流程序列圖:
3.2 授權流程(權限校驗)
授權流程是驗證用戶是否有權限執行某個操作的過程,核心是檢查“用戶擁有的權限”是否包含“操作所需的權限”。
流程步驟:
- 用戶通過 Subject 發起權限校驗請求(如訪問某接口)。
- Subject 將請求委托給 SecurityManager。
- SecurityManager 委托 Authorizer 處理授權。
- Authorizer 通過 Realm 從數據源獲取用戶的權限信息(角色/權限)。
- Authorizer 校驗用戶權限是否滿足操作需求,返回授權結果。
授權流程序列圖:
3.3 會話管理
Shiro 的會話管理是其核心功能之一,支持跨環境(Web/非Web)的會話統一管理,無需依賴容器(如Tomcat)的會話機制,且提供豐富的會話控制能力。
1. 會話管理核心組件
- SessionManager:管理所有會話的生命周期(創建、讀取、更新、刪除),默認實現為
DefaultSessionManager
(非Web環境)和ServletContainerSessionManager
(Web環境,依賴容器會話),推薦自定義DefaultWebSessionManager
實現獨立管理。 - SessionDAO:負責會話的持久化(如內存、數據庫、Redis等),常用實現:
MemorySessionDAO
:默認內存存儲(適合開發環境)。EnterpriseCacheSessionDAO
:緩存存儲(結合EhCache/Redis)。- 自定義DAO:集成Redis實現分布式會話(解決集群會話共享問題)。
- SessionListener:監聽會話創建、過期、停止等事件(如記錄用戶登錄日志)。
- SessionValidationScheduler:定期驗證會話有效性,清理過期會話(默認每30分鐘執行一次)。
2. 會話管理流程
3. 關鍵特性
- 會話標識(Session ID):自動生成唯一ID,可通過
session.getId()
獲取,常用于跟蹤用戶會話。 - 會話屬性:支持存儲自定義屬性(如
session.setAttribute("user", userInfo)
),實現用戶信息跨請求共享。 - 過期控制:可設置全局過期時間(如30分鐘無操作失效)或單個會話過期時間(
session.setTimeout(3600000)
)。 - 分布式支持:通過自定義
SessionDAO
集成Redis,實現多服務器間會話共享(解決集群環境下登錄狀態同步問題)。
3.4 加密
Shiro 提供簡化的加密工具,封裝了常見加密算法,避免直接操作復雜的Java加密API,常用于密碼加密存儲。
1. 核心加密組件
- HashService:提供哈希計算(如MD5、SHA-256),
SimpleHash
是常用實現,需指定算法、原始數據、鹽值、哈希次數。 - 鹽值(Salt):隨機生成的字符串,與密碼混合后哈希(防止彩虹表破解,如
salt = new SecureRandomNumberGenerator().nextBytes()
)。 - CredentialsMatcher:驗證用戶提交的憑證(如密碼)與存儲的哈希值是否匹配(如
HashedCredentialsMatcher
)。
2. 密碼加密與驗證流程
3. 關于哈希和鹽值的補充說明
在Shiro等安全框架中,哈希(Hash) 和鹽值(Salt) 是保護密碼的核心技術,用于將明文密碼轉換為不可逆的密文,同時抵御常見的破解手段。下面通過原理拆解和流程示例,清晰說明它們的作用:
-
哈希(Hash):將密碼"單向轉換"為密文
哈希算法(如MD5、SHA-256、Shiro中的SimpleHash
)是一種單向加密函數,它能將任意長度的輸入(如明文密碼)轉換為固定長度的輸出(哈希值,即密文),且無法從哈希值反推回明文。 -
哈希的核心作用:
- 不可逆性 :
例如,明文"123456"
通過SHA-256哈希后可能得到"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"
,但無法從這個哈希值反算出"123456"
。這意味著即使數據庫泄露,攻擊者也無法直接獲取明文密碼。 - 固定長度輸出:
無論輸入是1個字符還是1000個字符,哈希值長度固定(如SHA-256始終輸出64個字符),避免密碼長度泄露。 - 雪崩效應:
輸入的微小變化(如密碼從"123456"
改為"123457"
)會導致哈希值完全不同,進一步增強安全性。
- 不可逆性 :
-
鹽值(Salt):解決"相同密碼哈希值相同"的漏洞
哈希算法有一個問題:相同的明文會生成完全相同的哈希值。例如,兩個用戶都用"123456"
作為密碼,它們的哈希值完全一樣。這會導致兩個風險:- 攻擊者可通過"彩虹表"(預計算的常見密碼哈希值字典)快速匹配出明文;
- 一旦一個用戶密碼被破解,所有使用相同密碼的用戶都會暴露。
-
鹽值的作用:給每個密碼添加一個隨機唯一的字符串(鹽值),再進行哈希,確保即使明文相同,最終的哈希值也不同。
- 鹽值的特性:
- 隨機性:每個用戶的鹽值獨立生成,不可預測;
- 唯一性:通常與用戶賬號綁定(如存儲在用戶表中),確保同一用戶的鹽值固定;
- 公開性:鹽值不需要保密,可隨哈希值一起存儲(因為破解難度不在鹽值本身,而在加鹽后的哈希計算)。
- 鹽值的特性:
-
為什么哈希+鹽值能有效防破解?
- 抵御彩虹表攻擊:
彩虹表是預計算的"明文-哈希值"對應表,但加鹽后,相同明文的哈希值完全不同,攻擊者需要為每個可能的鹽值生成彩虹表,計算量呈指數級增長,幾乎不可能實現。 - 防止批量破解:
即使兩個用戶密碼相同,由于鹽值不同,哈希值也不同,破解一個用戶的密碼不會影響其他用戶。 - 增加暴力破解難度:
暴力破解(逐個嘗試明文并計算哈希)的效率極低,尤其是配合"哈希迭代次數"(如Shiro的hashIterations
,重復多次哈希計算),可進一步增加破解時間。
- 抵御彩虹表攻擊:
3.5 其他核心功能
1. 緩存(Caching)
- 作用:緩存用戶權限信息(如角色、權限),減少數據庫查詢,提升性能。
- 核心組件:
CacheManager
:管理緩存(默認集成EhCache,可替換為RedisCacheManager)。AuthorizingRealm
:通過doGetAuthorizationInfo
加載權限時自動緩存,默認緩存時間由緩存管理器配置。
- 使用場景:用戶登錄后,權限信息被緩存,后續授權操作直接從緩存獲取,直至緩存過期或用戶權限更新。
2. rememberMe(記住我)
- 功能:用戶勾選“記住我”后,關閉瀏覽器再打開,無需重新登錄即可訪問非敏感資源。
- 實現原理:
- 登錄成功后,Shiro生成加密的
rememberMe
Cookie(包含用戶標識),存儲在客戶端。 - 下次訪問時,Shiro讀取Cookie并自動登錄(但
subject.isAuthenticated()
為false
,subject.isRemembered()
為true
)。
- 登錄成功后,Shiro生成加密的
- 安全控制:敏感操作(如支付)需強制要求用戶重新認證(
subject.isAuthenticated() == true
)。
3. 并發登錄控制
- 功能:限制同一用戶同時登錄的設備數量(如只允許一個設備在線)。
- 實現方式:
- 自定義
SessionListener
,登錄時檢查該用戶的活躍會話數量。 - 超過限制時,強制踢出舊會話(
oldSession.stop()
)。
- 自定義
四、Shiro 核心組件詳解
4.1 Subject
- 定義:代表當前“用戶”(可以是真實用戶、進程、服務等),是開發者與 Shiro 交互的入口。
- 常用方法:
login(AuthenticationToken token)
:登錄(提交認證)。isAuthenticated()
:判斷是否已認證(登錄成功)。hasRole(String role)
:判斷是否擁有某個角色。checkPermission(String permission)
:校驗是否擁有某個權限(無權限時拋異常)。getSession()
:獲取當前會話。
4.2 SecurityManager
- 定義:Shiro 的核心管理器,協調所有組件(認證、授權、會話等),開發者需先初始化并配置它。
- 初始化示例(通過 Ini 配置文件):
// 讀取 Shiro 配置文件 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); // 設置全局 SecurityManager SecurityUtils.setSecurityManager(securityManager);
4.3 Realm
-
定義:連接應用與數據源的橋梁,負責提供用戶信息(認證)和權限信息(授權)。
-
常見類型:
IniRealm
:從 ini 配置文件讀取用戶/權限數據(適合簡單場景)。JdbcRealm
:從數據庫讀取數據(需配置 SQL 語句)。- 自定義 Realm:繼承
AuthorizingRealm
,重寫認證和授權方法(適合復雜業務)。
-
自定義 Realm 示例:
public class MyRealm extends AuthorizingRealm {// 授權:獲取用戶權限@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = (String) principals.getPrimaryPrincipal();// 從數據庫查詢用戶權限(示例)Set<String> permissions = new HashSet<>();permissions.add("user:query");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permissions);return info;}// 認證:獲取用戶憑證@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String) token.getPrincipal();// 從數據庫查詢密碼(示例)String password = "123456"; // 實際應從 DB 讀取return new SimpleAuthenticationInfo(username, password, getName());} }
五、Shiro 與 Web 集成
在 Web 應用中,Shiro 通過 Filter 攔截請求,實現 URL 級別的權限控制。
核心配置(web.xml):
<!-- Shiro 過濾器 -->
<filter><filter-name>shiroFilter</filter-name><filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
URL 權限配置(shiro.ini):
[urls]
/login = anon # 登錄頁允許匿名訪問
/logout = logout # 退出登錄
/user/** = authc, perms["user:manage"] # /user/** 路徑需認證且擁有 user:manage 權限
/admin/** = authc, roles["admin"] # /admin/** 路徑需認證且擁有 admin 角色
六、同類型產品分析
在Java生態中,主流的安全框架以 Apache Shiro 和 Spring Security 為主,此外還有針對特定場景的解決方案(如Keycloak、Pac4j等)。選擇框架時需結合項目規模、技術棧、安全需求復雜度等因素。
主流方案對比:Shiro vs Spring Security
兩者是最常用的企業級安全框架,核心功能覆蓋認證、授權、加密等,但設計理念和適用場景差異顯著。
對比維度 | Apache Shiro | Spring Security |
---|---|---|
設計理念 | 簡潔、易用,強調“開箱即用”,API直觀易懂 | 功能全面,強調“深度集成Spring生態”,靈活性強 |
核心功能 | 認證、授權、會話管理、加密、緩存集成 | 認證、授權、OAuth2.0/OpenID Connect、SSO、LDAP等(功能更全) |
易用性 | 學習曲線平緩,配置簡單(XML/注解均可),文檔清晰 | 學習曲線較陡,配置復雜(依賴Spring生態知識) |
集成性 | 與Spring、Java EE、Servlet、JAX-RS等均可集成(不綁定特定框架) | 深度綁定Spring生態(Spring Boot/Cloud無縫集成),非Spring項目集成較麻煩 |
會話管理 | 內置獨立會話管理(不依賴Servlet容器),支持分布式會話 | 早期依賴Servlet容器會話,需配合Spring Session實現分布式 |
擴展能力 | 擴展點明確(如Realm、Filter),適合簡單擴展 | 擴展點極多(如SecurityContext、AuthenticationProvider),適合復雜場景定制 |
適用場景 | 中小型應用、快速開發、多框架集成、非Spring項目 | 大型企業應用、Spring生態項目、復雜安全需求(如SSO、OAuth2) |
社區活躍度 | 社區穩定,更新頻率中等(2023年發布1.12.0版本) | 社區活躍,更新頻繁(依賴Spring版本迭代) |
Shiro是“簡單場景的最優解”,以低學習成本滿足基礎安全需求;Spring Security是“復雜場景的全能選手”,適合深度集成Spring生態的大型項目。選型時不必糾結“功能多少”,而應聚焦“項目實際需求”和“團隊技術棧匹配度”——合適的才是最好的。