Apache Shiro 是 Java 的一個安全框架。相比 Spring Security,可能沒有 Spring Security 做的功能強大,但是在實際工作時可能並不需要那麼複雜的東西,所以使用小而簡單的 Shiro 就足夠了,而且 Shiro 的 API 也是非常簡單。
Authentication
:身份認證/登錄,驗證用戶是不是擁有相應的身份;
Authorization
:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;
Session Manager
:會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有信息都在會話中;
Remember Me
:記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登錄了。
Shiro 不會去維護用戶、維護權限;這些需要我們自己去設計/提供;然後通過相應的接口注入給 Shiro 即可。
對於一個好的框架,從外部來看應該具有非常簡單易於使用的 API,從內部來看的話,其應該有一個可擴展的架構,即非常容易插入用戶自定義實現。
從外部看
1、 應用代碼通過 Subject 來進行認證和授權,而 Subject 又委託給 SecurityManager;
2、 我們需要給 Shiro 的 SecurityManager 注入 Realm,從而讓 SecurityManager 能得到合法的用戶及其權限進行判斷。
在 shiro 中,用戶需要提供 principals (身份)和 credentials(證明)給 shiro,讓應用來驗證用戶身份
principals
:身份,可以是用戶名、郵箱等,唯一即可。
credentials
:證明/憑證,即只有主體知道的安全值,如密碼/數字證書等。
最常見的 principals 和 credentials 組合就是用戶名/密碼了。
Shiro 從Realm 獲取安全數據(如用戶、角色、權限),SecurityManager要驗證用戶身份,需要從 Realm 獲取用戶信息進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應的角色/權限以驗證用戶是否能進行操作;可以把 Realm 看成 DataSource,即安全數據源
。
1、UserRealm 父類 AuthorizingRealm 將獲取 Subject 相關信息分成兩步:獲取身份驗證信息(doGetAuthenticationInfo)及授權信息(doGetAuthorizationInfo);
2、doGetAuthenticationInfo() 獲取身份驗證相關信息:
首先根據傳入的用戶名獲取 User 信息;然後如果 user 爲空,拋出沒找到帳號異常 UnknownAccountException;
如果 user找到但鎖定了拋出鎖定異常 LockedAccountException;
最後生成 AuthenticationInfo
信息,交給間接父類 AuthenticatingRealm 使用 CredentialsMatcher 進行判斷密碼是否匹配,
如果不匹配將拋出密碼錯誤異常 IncorrectCredentialsException;
另外如果密碼重試此處太多將拋出超出重試次數異常 ExcessiveAttemptsException;
組裝 SimpleAuthenticationInfo() 信息並返回
3、doGetAuthorizationInfo 獲取授權信息:
PrincipalCollection 是一個身份集合,因爲我們現在就一個 Realm,所以直接調用 getPrimaryPrincipal 得到之前傳入的用戶名即可;然後根據用戶名調用 UserService 接口獲取角色及權限信息。
AuthenticationInfo 有兩個作用:
1、如果 Realm 是 AuthenticatingRealm 子類,則提供給 AuthenticatingRealm 內部使用的CredentialsMatcher 進行憑據驗證;
2、提供給 SecurityManager 來創建 Subject(提供身份信息);
PrincipalCollection作用:
Shiro 中同時配置多個 Realm,所以身份信息可能就有多個;因此其提供了 PrincipalCollection 用於聚合這些身份信息
在授權中需瞭解的幾個關鍵對象:主體(Subject
)、資源(Resource
)、權限(Permission
)、角色(Role
)。
主體,即訪問應用的用戶,在 Shiro 中使用 Subject 代表該用戶。
Shiro 支持三種方式的授權
1、編程式
Subject subject = SecurityUtils.getSubject(); if(subject.hasRole(「admin」)) { //有權限 } else { //無權限 }
2、註解式
@RequiresRoles("admin") public void hello() { //有權限 }
3、在jsp中使用標籤
訪問控制包括基於角色授權和基於資源授權
授權流程如下:
1、首先調用 Subject.isPermitted*/hasRole* 接口,其會委託給 SecurityManager,而SecurityManager 接着會委託給 Authorizer;
2、Authorizer 是真正的授權者,如果我們調用如 isPermitted(「user:view」),其首先會通過PermissionResolver 把字符串轉換成相應的 Permission 實例;
3、在進行授權之前,其會調用相應的 Realm 獲取 Subject 相應的角色/權限用於匹配傳入的角色/權限;
4、Authorizer 會判斷 Realm 的角色/權限是否和傳入的匹配,如果有多個 Realm,會委託給ModularRealmAuthorizer 進行循環判斷,如果匹配如 isPermitted*/hasRole*會返回 true,否則返回 false 表示授權失敗。
Authorizer
的職責是進行授權(訪問控制),是 Shiro API 中授權核心的入口點,其提供了相應的角色/權限判斷接口。
SecurityManager
繼承了 Authorizer 接口,且提供了 ModularRealmAuthorizer 用於多 Realm 時的授權匹配。
PermissionResolver
用於解析權限字符串到 Permission 實例,而RolePermissionResolver 用於根據角色解析相應的權限集合。