What is Apache Shiro?
????Apache Shiro是一個功能強(qiáng)大屡立、靈活的,開源的安全框架镰吆。它可以干凈利落地處理身份驗證护盈、授權(quán)、企業(yè)會話管理和加密闲先。
????Apache Shiro的首要目標(biāo)是易于使用和理解状土。安全通常很復(fù)雜,甚至讓人感到很痛苦伺糠,但是Shiro卻不是這樣子的蒙谓。一個好的安全框架應(yīng)該屏蔽復(fù)雜性,向外暴露簡單训桶、直觀的API累驮,來簡化開發(fā)人員實現(xiàn)應(yīng)用程序安全所花費的時間和精力酣倾。
Shiro能做什么呢?
- 驗證用戶身份
- 用戶訪問權(quán)限控制谤专,比如:1躁锡、判斷用戶是否分配了一定的安全角色。2置侍、判斷用戶是否被授予完成某個操作的權(quán)限
- 在非 web 或 EJB 容器的環(huán)境下可以任意使用Session API
- 可以響應(yīng)認(rèn)證映之、訪問控制,或者 Session 生命周期中發(fā)生的事件
- 可將一個或以上用戶安全數(shù)據(jù)源數(shù)據(jù)組合成一個復(fù)合的用戶 “view”(視圖)
- 支持單點登錄(SSO)功能
- 支持提供“Remember Me”服務(wù)墅垮,獲取用戶關(guān)聯(lián)信息而無需登錄
…
????等等——都集成到一個有凝聚力的易于使用的API惕医。
????Shiro 致力在所有應(yīng)用環(huán)境下實現(xiàn)上述功能,小到命令行應(yīng)用程序算色,大到企業(yè)應(yīng)用中,而且不需要借助第三方框架螟够、容器灾梦、應(yīng)用服務(wù)器等。當(dāng)然 Shiro 的目的是盡量的融入到這樣的應(yīng)用環(huán)境中去妓笙,但也可以在它們之外的任何環(huán)境下開箱即用若河。
Apache Shiro Features 特性
????Apache Shiro是一個全面的、蘊(yùn)含豐富功能的安全框架寞宫。下圖為描述Shiro功能的框架圖:
????Authentication(認(rèn)證), Authorization(授權(quán)), Session Management(會話管理), Cryptography(加密)被 Shiro 框架的開發(fā)團(tuán)隊稱之為應(yīng)用安全的四大基石萧福。那么就讓我們來看看它們吧:
- Authentication(認(rèn)證):用戶身份識別,通常被稱為用戶“登錄”
- Authorization(授權(quán)):訪問控制辈赋。比如某個用戶是否具有某個操作的使用權(quán)限鲫忍。
- Session Management(會話管理):特定于用戶的會話管理,甚至在非web 或 EJB 應(yīng)用程序。
- Cryptography(加密):在對數(shù)據(jù)源使用加密算法加密的同時钥屈,保證易于使用悟民。
????還有其他的功能來支持和加強(qiáng)這些不同應(yīng)用環(huán)境下安全領(lǐng)域的關(guān)注點。特別是對以下的功能支持
- Web支持:Shiro 提供的 web 支持 api 篷就,可以很輕松的保護(hù) web 應(yīng)用程序的安全射亏。
- 緩存:緩存是 Apache Shiro 保證安全操作快速、高效的重要手段竭业。
- 并發(fā):Apache Shiro 支持多線程應(yīng)用程序的并發(fā)特性智润。
- 測試:支持單元測試和集成測試,確保代碼和預(yù)想的一樣安全未辆。
- “Run As”:這個功能允許用戶假設(shè)另一個用戶的身份(在許可的前提下)窟绷。
- “Remember Me”:跨 session 記錄用戶的身份,只有在強(qiáng)制需要時才需要登錄鼎姐。
注意: Shiro不會去維護(hù)用戶钾麸、維護(hù)權(quán)限更振,這些需要我們自己去設(shè)計/提供,然后通過相應(yīng)的接口注入給Shiro
High-Level Overview 高級概述
????在概念層饭尝,Shiro 架構(gòu)包含三個主要的理念:Subject,SecurityManager和 Realm肯腕。下面的圖展示了這些組件如何相互作用,我們將在下面依次對其進(jìn)行描述钥平。
- Subject:當(dāng)前用戶实撒,Subject 可以是一個人,但也可以是第三方服務(wù)涉瘾、守護(hù)進(jìn)程帳戶知态、時鐘守護(hù)任務(wù)或者其它–當(dāng)前和軟件交互的任何事件。
- SecurityManager:管理所有Subject立叛,SecurityManager 是 Shiro 架構(gòu)的核心负敏,配合內(nèi)部安全組件共同組成安全傘。
- Realms:用于進(jìn)行權(quán)限信息的驗證秘蛇,我們自己實現(xiàn)其做。Realm 本質(zhì)上是一個特定的安全 DAO:它封裝與數(shù)據(jù)源連接的細(xì)節(jié),得到Shiro 所需的相關(guān)的數(shù)據(jù)赁还。在配置 Shiro 的時候妖泄,你必須指定至少一個Realm 來實現(xiàn)認(rèn)證(authentication)和/或授權(quán)(authorization)。
????我們需要實現(xiàn)Realms的Authentication 和 Authorization艘策。其中 Authentication 是用來驗證用戶身份蹈胡,Authorization 是授權(quán)訪問控制,用于對用戶進(jìn)行的操作授權(quán)朋蔫,證明該用戶是否允許進(jìn)行當(dāng)前操作罚渐,如訪問某個鏈接,某個資源文件等斑举。
快速上手
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Shiro 配置
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//攔截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不會被攔截的鏈接 順序判斷
filterChainDefinitionMap.put("/static/**", "anon");
//配置退出 過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實現(xiàn)了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 過濾鏈定義搅轿,從上向下順序執(zhí)行,一般將/**放在最為下邊 -->:這是一個坑呢富玷,一不小心代碼就不好使了;
//<!-- authc:所有url都必須認(rèn)證通過才可以訪問; anon:所有url都都可以匿名訪問-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不設(shè)置默認(rèn)會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登錄成功后要跳轉(zhuǎn)的鏈接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授權(quán)界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
}
Filter Chain定義說明:
- 一個URL可以配置多個Filter璧坟,使用逗號分隔
- 當(dāng)設(shè)置多個過濾器時,全部驗證通過赎懦,才視為通過
- 部分過濾器可指定參數(shù)雀鹃,如perms,roles
Shiro內(nèi)置的FilterChain:
Filter Name | Class |
---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port | org.apache.shiro.web.filter.authz.PortFilter |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
ssl | org.apache.shiro.web.filter.authz.SslFilter |
user | org.apache.shiro.web.filter.authc.UserFilter |
- anon:所有url都都可以匿名訪問
- authc: 需要認(rèn)證才能進(jìn)行訪問
- user:配置記住我或認(rèn)證通過可以訪問
登錄認(rèn)證實現(xiàn)
????在認(rèn)證励两、授權(quán)內(nèi)部實現(xiàn)機(jī)制中都有提到黎茎,最終處理都將交給Real進(jìn)行處理。因為在Shiro中当悔,最終是通過Realm來獲取應(yīng)用程序中的用戶傅瞻、角色及權(quán)限信息的踢代。通常情況下,在Realm中會直接從我們的數(shù)據(jù)源中獲取Shiro需要的驗證信息嗅骄「炜妫可以說,Realm是專用于安全框架的DAO.
Shiro的認(rèn)證過程最終會交由Realm執(zhí)行溺森,這時會調(diào)用Realm的getAuthenticationInfo(token)方法慕爬。
????該方法主要執(zhí)行以下操作:
- 檢查提交的進(jìn)行認(rèn)證的令牌信息
- 根據(jù)令牌信息從數(shù)據(jù)源(通常為數(shù)據(jù)庫)中獲取用戶信息
- 對用戶信息進(jìn)行匹配驗證。
- 驗證通過將返回一個封裝了用戶信息的AuthenticationInfo實例屏积。
- 驗證失敗則拋出AuthenticationException異常信息医窿。
????而在我們的應(yīng)用程序中要做的就是自定義一個Realm類,繼承AuthorizingRealm抽象類炊林,重載doGetAuthenticationInfo()姥卢,重寫獲取用戶信息的方法。
doGetAuthenticationInfo的重寫
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
//獲取用戶的輸入的賬號.
String username = (String)token.getPrincipal();
System.out.println(token.getCredentials());
//通過username從數(shù)據(jù)庫中查找 User對象铛铁,如果找到隔显,沒找到.
//實際項目中,這里可以根據(jù)實際情況做緩存饵逐,如果不做,Shiro自己也是有時間間隔機(jī)制彪标,2分鐘內(nèi)不會重復(fù)執(zhí)行該方法
UserInfo userInfo = userInfoService.findByUsername(username);
System.out.println("----->>userInfo="+userInfo);
if(userInfo == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo, //用戶名
userInfo.getPassword(), //密碼
ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
return authenticationInfo;
}
鏈接權(quán)限的實現(xiàn)
????shiro的權(quán)限授權(quán)是通過繼承AuthorizingRealm抽象類倍权,重載doGetAuthorizationInfo()當(dāng)訪問到頁面的時候,鏈接配置了相應(yīng)的權(quán)限或者shiro標(biāo)簽才會執(zhí)行此方法否則不會執(zhí)行捞烟,所以如果只是簡單的身份認(rèn)證沒有權(quán)限的控制的話薄声,那么這個方法可以不進(jìn)行實現(xiàn),直接返回null即可题画。在這個方法中主要是使用類:SimpleAuthorizationInfo進(jìn)行角色的添加和權(quán)限的添加默辨。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
UserInfo userInfo = (UserInfo)principals.getPrimaryPrincipal();
for(SysRole role:userInfo.getRoleList()){
authorizationInfo.addRole(role.getRole());
for(SysPermission p:role.getPermissions()){
authorizationInfo.addStringPermission(p.getPermission());
}
}
return authorizationInfo;
}
????當(dāng)然也可以添加set集合:roles是從數(shù)據(jù)庫查詢的當(dāng)前用戶的角色,stringPermissions是從數(shù)據(jù)庫查詢的當(dāng)前用戶對應(yīng)的權(quán)限
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(stringPermissions);
????就是說如果在shiro配置文件中添加了 filterChainDefinitionMap.put(“/add”, “perms[權(quán)限添加]”) 就說明訪問 /add 這個鏈接必須要有“權(quán)限添加”這個權(quán)限才可以訪問苍息,如果在shiro配置文件中添加了 filterChainDefinitionMap.put(“/add”, “roles[100002]缩幸,perms[權(quán)限添加]”) 就說明訪問 /add 這個鏈接必須要有“權(quán)限添加”這個權(quán)限和具有“100002”這個角色才可以訪問。
參考附錄: