記得做的第一個Web項目,系統認證授權這塊就是基于Shiro實現的,當時也是第一次接觸到這種類型的框架,同時是基于Spring做的集成,并且相關的配置都已經是定制好的,只需要我們根據項目情況對極少部分配置進行修改即可使用。對于其原理也只是知道個大概,具體實現細節可以說是一無所知。
Shiro作為安全框架,相比其他同類型框架,在整體結構上更顯得簡單且更易于理解,通過對Shrio的學習,在理解里其實現原理后,對后面學習其他同類型的框架也會有很多幫助。
現在各種框架盛行,為解決企業問題提供了各種優秀的解決方案,使開發者可以快速、簡單、高效地搭建開發環境,用起來確實很方便,但是卻產生了強烈的依賴性,現在有多少人脫離了框架還能開發出想要的功能?當然,框架我們還是要用的,但是隨著學習的不斷加深,在出現故障時我們及時定位問題,在當前業務需求不滿足時,我們需要快速找到解決方法。
功能介紹
以下是官網的一段話,直接介紹了Shiro能為我們做什么
Apache Shiro是一個功能強大且易于使用的Java安全框架,可執行身份驗證、授權、加密和會話管理。
特色
主要功能:
- Authentication:身份認證,證明用戶是否具有訪問系統的身份。
- Authorization:訪問控制,即確定“誰”可以訪問系統的“什么”資源。
- Session Management:會話管理,支持Web 或 EJB 應用程序的會話管理。
- Cryptography:使用密碼算法保持數據安全,主要是密碼。
其他功能:
實現架構
架構
- Subject( org.apache.shiro.subject.Subject) 當前與軟件交互的實體(用戶、第三方服務、cron任務等)的特定的安全性的“視圖”。
- SecurityManager ( org.apache.shiro.mgt.SecurityManager ) SecurityManager是Shiro架構的核心。它主要是一個綜合的大對象,用于協調其托管組件以確保它們順利協同工作。它還管理Shiro對每個應用程序用戶的視圖,因此它知道如何為每個用戶執行安全操作。
- Authenticator ( org.apache.shiro.authc.Authenticator ) 該Authenticator組件負責執行和響應用戶的身份驗證過程。當用戶嘗試登錄時,該邏輯由Authenticator實現并執行。Authenticator通過用戶配置的多個Realms,按照一定的策略,驗證提供的用戶是否滿足用戶身份。
- AuthenticationStrategy ( org.apache.shiro.authc.pam.AuthenticationStrategy ) 當配置了多個Realm時,AuthenticationStrategy將協調調用Realm來確定用戶身份驗證最終成功或失敗(比如有一個驗證成功或每個都要驗證成功)。
- Authorizer ( org.apache.shiro.authz.Authorizer ) 是一個應用訪問控制的組件。它是決定用戶是否被允許執行某項操作。與Authenticator一樣,Authorizer能夠協調執行多個Realm來獲取用戶的訪問角色和權限信息。
- SessionManager ( org.apache.shiro.session.mgt.SessionManager ) 負責創建和管理用戶Session的生命周期。即使沒有可用的Web容器,Shiro能夠在任何環境中本地管理用戶會話。默認情況下是基于Web容器實現會話機制,非Web環境則可以通過內置的會話管理來提供相同的功能。可以通過SessionDAO實現會話的持久化。
- SessionDAO ( org.apache.shiro.session.mgt.eis.SessionDAO ) 實現Session的持久化。
- CacheManager ( org.apache.shiro.cache.CacheManager ) 因為Shiro經常訪問后端數據源進行身份驗證、授權和會話管理,通過緩存可以提高性能。
- Cryptography( org.apache.shiro.crypto.* ) 密碼學是企業安全框架的擴展。Shiro的crypto包含易于使用和理解的密碼、哈希(又名摘要)和不同編解碼器實現的實現。Shiro的加密API簡化了復雜的Java機制,使密碼加密更容易。
- Realm ( org.apache.shiro.realm.Realm ) Realm是Shiro和應用程序安全數據之間的“橋梁”。當用戶需要對身份進行認證或授權時,Shiro會通過程序中配置的一個或多個Realm來查找滿足的數據。Shiro將根據需要與它們協調以進行身份驗證和授權。
相關術語
如果理解了官網里介紹的這些術語,那么在進行shiro配置時,需要配置哪些信息,,分別有什么作用,怎么配置應該也會有一個較好的認識。
Authentication是驗證主體身份的過程——本質上是證明某人確實是他們所說的那個人。當身份驗證嘗試成功時,應用程序可以相信該主體是應用程序所期望的。
Authorization授權,也稱為訪問控制,是確定是否允許用戶/主體做某事的過程。它通常通過檢查和解釋主題的角色和權限(見下文)然后允許或拒絕訪問請求的資源或功能來完成。
Cipher密碼是一種用于執行加密或解密的算法。該算法通常依賴于稱為密鑰的一條信息。并且加密會根據密鑰而有所不同,因此如果沒有它,解密將非常困難。
密碼有不同的變體。塊密碼適用于通常具有固定大小的符號塊,而流密碼適用于連續的符號流。對稱密碼使用相同的密鑰進行加密和解密,而非對稱密碼使用不同的密鑰。如果非對稱密碼中的密鑰不能從另一個密鑰導出,則可以公開共享一個密鑰,創建公鑰/私鑰對
Credential憑據是驗證用戶/主體身份的一條信息 。在驗證嘗試驗證提交它們的用戶/主體實際上是關聯用戶期間,一個(或多個)憑證與主體一起提交。憑證通常是只有特定用戶/主體才能知道的非常機密的東西,例如密碼或 PGP 密鑰或生物特征或類似機制。
這個想法是,對于委托人,只有一個人會知道與該委托人“配對”的正確憑證。如果當前用戶/主題提供了與存儲在系統中的證書匹配的正確證書,那么系統可以假定并相信當前用戶/主題確實是他們所說的那個人。信任程度隨著更安全的憑證類型(例如生物特征簽名 > 密碼)而增加。
Cryptography密碼學是一種通過隱藏信息或將其轉換為無意義的信息來保護信息免遭不受歡迎的訪問的做法,這樣其他人就無法閱讀它。Shiro 專注于密碼學的兩個核心元素:使用公鑰或私鑰對電子郵件等數據進行加密的密碼,以及對密碼等數據進行不可逆加密的哈希(又名消息摘要)。
Hash散列函數是將輸入源(有時稱為消息)單向、不可逆地轉換為編碼散列值(有時稱為消息摘要)。它通常用于密碼、數字指紋或具有底層字節數組的數據。
Permission至少在 Shiro 的解釋中,權限是描述應用程序中原始功能的語句,僅此而已。權限是安全策略中最低級別的結構。他們只定義應用程序可以做什么。他們沒有描述“誰”能夠執行這些操作。許可只是行為聲明,僅此而已。
Principal主體是應用程序用戶(主題)的任何標識屬性 “識別屬性”可以是任何對您的應用程序有意義的東西——用戶名、姓氏、名字、社會安全號碼、用戶 ID 等等。就是這樣——沒什么瘋狂的。
Realm領域是可以訪問特定于應用程序的安全數據(例如用戶、角色和權限)的組件。它可以被認為是一個安全特定的 DAO(數據訪問對象)。Realm 將這種特定于應用程序的數據轉換為 Shiro 可以理解的格式,因此無論存在多少數據源或您的數據可能是多么特定于應用程序,Shiro 都可以反過來提供一個易于理解的主題編程 API。
領域通常與數據源(例如關系數據庫、LDAP 目錄、文件系統或其他類似資源)具有一對一的關聯。因此,Realm 接口的實現使用特定于數據源的 API 來發現授權數據(角色、權限等),例如 JDBC、文件 IO、Hibernate 或 JPA,或任何其他數據訪問 API。
Role角色的定義與交互的系統關聯。在許多應用程序中,人們使用它來隱式定義安全策略充其量只是一個模糊的概念。Shiro 更喜歡將角色簡單地解釋為權限的命名集合。就是這樣 - 聚合一個或多個權限聲明的應用程序唯一名稱.
這是一個比許多應用程序使用的隱含定義更具體的定義。如果您選擇讓您的數據模型反映 Shiro 的假設,您會發現您將擁有更多控制安全策略的權力。
Session會話是與在一段時間內與軟件系統交互的單個用戶/主體相關聯的有狀態數據上下文。當主體使用應用程序時,可以從會話中添加/讀取/刪除數據,并且應用程序可以稍后在必要時使用這些數據。當用戶/主體退出應用程序或由于不活動而超時時,會話將終止。
對于熟悉 HttpSession 的人來說,ShiroSession具有相同的用途,除了 Shiro 會話可以在任何環境中使用,即使沒有可用的 Servlet 容器或 EJB 容器。
Subject主題只是花哨的安全術語,基本上意味著應用程序用戶的特定于安全的“視圖” 。一個主題并不總是需要反映一個人 - 它可以代表一個調用您的應用程序的外部進程,或者可能是一個在一段時間內間歇性執行某些事情的守護程序系統帳戶(例如 cron 作業)。它基本上是對應用程序做某事的任何實體的表示。
關鍵模塊
EnvironmentLoaderListener
該類實現了ServletContextListener,因此我們在web.xml中配置后即可啟用,主要目的是為了構建Enviroment對象并保存到ServleteContext中。
public class EnvironmentLoaderListener extends EnvironmentLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
initEnvironment(sce.getServletContext());
}
public void contextDestroyed(ServletContextEvent sce) {
destroyEnvironment(sce.getServletContext());
}
}
Environment
Environment是一個Shiro容器,主要目的是提供SecurityManager對象。
public interface Environment {
SecurityManager getSecurityManager();
}
SecurityManager
SecurityManager是Shiro框架中最核心的類,用戶的認證、鑒權都是基于它來完成的,包含認證、會話管理、RememberMe等功能,這也是開發者需要理解并配置較多的類。
public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
void logout(Subject subject);
Subject createSubject(SubjectContext context);
}
Authenticator
實現了用戶的身份認證邏輯,也就是密碼校驗,注意我們是通過對AuthenticationToken進行認證,在該Token中封裝了用戶提交的信息,
開發者只需要根據這些信息構建AuthenticationInfo對象,密碼的校驗過程是有Shiro代理完成的。
public interface Authenticator {
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
throws AuthenticationException;
}
Authorizer
實現了用戶權限的鑒權,包括權限和角色,鑒權過程在整個系統的生命周期中是比較頻繁的,我們應該配置CacheManager來提高系統效率,避免頻繁的進行DAO操作。
public interface Authorizer {
boolean isPermitted(PrincipalCollection principals, String permission);
boolean isPermittedAll(PrincipalCollection subjectPrincipal, String... permissions);
void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException;
boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier);
boolean hasAllRoles(PrincipalCollection subjectPrincipal, Collection< String > roleIdentifiers);
void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException;
// ...
}
SessionManager
會話管理主要是為了實現認證保持,即一次認證成功后基于Cookie/Session機制,使下次會話不需要重復認證。在集群環境中,如果通過容器來管理會話那樣就沒辦法實現會話共享,我們可以通過一些分布式會話管理框架來實現。
public interface SessionManager {
Session start(SessionContext context);
Session getSession(SessionKey key) throws SessionException;
}
SubjectDAO實現Subject對象的保存,比如將Subject保存的Session或者到數據庫。一般情況下,我們通過 SecurityUtils.getSubject()獲取Subject對象,這里沒有提供get方法。
public interface SubjectDAO {
Subject save(Subject subject);
void delete(Subject subject);
}
RememberMeManager
記住我,實現用戶在會話超時后,不需要重新登錄即可進入系統,默認是基于Cookie實現,主要將用戶信息寫入到Cookie中,并設置一個有效期,比如14天。
public interface RememberMeManager {
PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext);
void forgetIdentity(SubjectContext subjectContext);
void onSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info);
void onFailedLogin(Subject subject, AuthenticationToken token, AuthenticationException ae);
void onLogout(Subject subject);
}
CacheManager
緩存,上面說到在鑒權時通過緩存提高API效率,還可以緩存Session實現Session快速存取。
public interface CacheManager {
< K, V > Cache< K, V > getCache(String var1) throws CacheException;
}
Realm
Realm是開發者經常需要重寫的,也是Shiro的擴展點,我們可以根據需要配置多個Realm,這樣可以實現多因子認證,比如用戶名、郵箱、指紋等。一般可以通過繼承AuthenticatingRealm重寫抽象方法實現。
public interface Realm {
String getName();
boolean supports(AuthenticationToken token);
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}
Subject
Subject也是Shiro中非常重要的對象,在系統中,我們的操作基本都是調用其內部方法,最終由SecurityManager代理完成。比如常見的登錄、登出、是否有角色、是否有權限等。
public interface Subject {
Object getPrincipal();
PrincipalCollection getPrincipals();
void login(AuthenticationToken token) throws AuthenticationException;
// ...
}
AuthenticationToken
用戶提交的請求數據我們一般會進行封裝,因為除了用戶名密碼,可能還會有一些其他信息,比如動態碼、RememberMe標識,或者基于令牌的認證。
public interface AuthenticationToken extends Serializable {
Object getPrincipal();
Object getCredentials();
}
SubjectContext
Subject上下文,保存了Subject相關的數據,比如Subject、SecurityManager、Session等,在Subject間相互隔離。
public interface SubjectContext extends Map< String, Object > {
Subject getSubject();
PrincipalCollection getPrincipals();
PrincipalCollection resolvePrincipals();
Session getSession();
Session resolveSession();
boolean isAuthenticated();
boolean isSessionCreationEnabled();
boolean resolveAuthenticated();
AuthenticationInfo getAuthenticationInfo();
AuthenticationToken getAuthenticationToken();
}
ShiroFilter
Shiro過濾器鏈,前面一直提到基于Web的認證基本都是通過Filter實現,在一個Web項目中會有很多個Filter,而且會按照配置順序執行,隱藏Shiro的過濾器會定義成鏈的模式,同時也是為了方便配置。開發者會根據業務需求配置過濾規則,最終都會由FilterChainResolver解析成過濾器鏈。
public class ShiroFilter extends AbstractShiroFilter {
@Override
public void init() throws Exception {
WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext());
setSecurityManager(env.getWebSecurityManager());
FilterChainResolver resolver = env.getFilterChainResolver();
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
}
Filter
在DefaultFilter中,我們可以看到這樣的定義,主要是為過濾器提供了別名方便開發者進行配置。
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
authcBearer(BearerHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class),
invalidRequest(InvalidRequestFilter.class);
一般配置如下:
/login.html = anon; //表示不需認證也可訪問
/login.html = authc; //表示表單POST提交到/login.html請求時會根據提交的參數進行身份認證
/system = authcBasic; //根據請求頭Authorization:BASIC Base64(用戶名:密碼)的形式完成認證
/system = authcBearer; //根據請求頭Authorization:Bearer Token的形式完成認證
/logout = logout; //退出登錄
/authz = perms[update, :add, :delete: ]; //表示具有操作權限可訪問,支持' '通配符
/index.html = port[8080]; //表示8080請求端口才能訪問
//權限字符串由 type:action:instance 組成,在rest服務中,通過權限與請求方法構建權限字符串[perm:mapping],
請求方法與action對應關系如下:
// GET|HEAD|OPTIONS|TRACE -> read
// PUT -> update
// POST|MKCOL -> create
/ = rest
認證流程
認證流程
第1步:應用程序代碼調用該Subject.login方法,傳入代表最終用戶的主體和憑據的構造AuthenticationToken實例。
第2步:Subject實例,通常是一個類通過調用DelegatingSubject委托給應用程序,實際的身份驗證工作從這里開始。SecurityManagersecurityManager.login(token)
第3步:SecurityManager作為基本的綜合組件,接收令牌并通過調用簡單地委托給其內部Authenticator實例authenticator.authenticate(token)。一般是ModularRealmAuthenticator實例,它支持在身份驗證期間協調一個或多個Realm實例。ModularRealmAuthenticator本質上為 Apache Shiro提供了PAM樣式的范例(其中每個Realm 都是 PAM 術語中的“模塊”)。
第4步:如果Realm為應用程序配置了多個,ModularRealmAuthenticator實例將Realm使用其配置的 啟動多重身份驗證嘗試策略AuthenticationStrategy。Realms在調用 進行身份驗證之前、期間和之后,AuthenticationStrategy將根據每個Realm的執行結果做出最終認證的決定。
第5步:根據每一個配置Realm,判斷supports是否提交AuthenticationToken。如果匹配則以token為參數調用Realm的getAuthenticationInfo方法。該方法代表了Realm的一次身份驗證嘗試。
結束語
關于shiro,很多都是介紹框架關鍵組件,比如SecurityManager,大部分功能都是圍繞該類展開,后面會通過實戰,一步一步了解以上提到的各個模塊在項目運行期間如何實現最終的用戶身份認證以及對應的訪問權限控制,以及細化到其具體的實現流程與模塊間的時序調用關系。
-
JAVA
+關注
關注
19文章
2966瀏覽量
104702 -
框架
+關注
關注
0文章
403瀏覽量
17475 -
加密
+關注
關注
0文章
304瀏覽量
23914 -
應用程序
+關注
關注
37文章
3265瀏覽量
57679
發布評論請先 登錄
相關推薦
評論