之間工作中曾經(jīng)用到過shiro這個(gè)權(quán)限控制的框架,之前一直都是停留在用的方面,沒有過多的 去理解這方面的知識(shí),現(xiàn)在有時(shí)間,專門研究了一下這個(gè)Shiro權(quán)限的框架使用。
Shiro是什么?
Apache Shiro是一個(gè)強(qiáng)大而靈活的開源安全框架,它干凈利落地處理身份認(rèn)證,授權(quán),企業(yè)會(huì)話管理和加密。
Apache Shiro的首要目標(biāo)是易于使用和理解。安全有時(shí)候是很復(fù)雜的,甚至是痛苦的,但它沒有必要這樣。框架應(yīng)該盡可能掩蓋復(fù)雜的地方,露出一個(gè)干凈而直觀的API,來簡(jiǎn)化開發(fā)人員在使他們的應(yīng)用程序安全上的努力。
因?yàn)楹芏鄷r(shí)候很多人比較喜歡使用Spring提供的Spring Security和Shiro來進(jìn)行對(duì)比,我們也對(duì)比一下這個(gè)內(nèi)容。
Apache Shiro是JAVA的一個(gè)安全框架。
目前,使用Apache Shiro的人越來越多,因?yàn)樗喈?dāng)簡(jiǎn)單,對(duì)比Spring Security,可能沒有Spring Security做的功能強(qiáng)大,但是在實(shí)際工作時(shí)可能并不需要那么復(fù)雜的東西,所以使用小而簡(jiǎn)單的Shiro就足夠了。
所以,我們工作中很多時(shí)候都是直接使用的Shiro來進(jìn)行安全控制。
我們介紹完了之后,在看看Shiro有什么用處?
Shiro作用
- 驗(yàn)證用戶來核實(shí)他們的身份
- 對(duì)用戶執(zhí)行訪問控制
比如說我們可以用來判斷一個(gè)用戶是否被分配了一個(gè)安全的角色。然后來判斷這個(gè)人到底在項(xiàng)目中能有什么權(quán)限來處理, 意思就是假如說有2個(gè)人,一個(gè)是管理員,他有增刪改查的功能,而另外一個(gè)用戶就是只又查的功能,沒有增刪改的功能, 通過Shiro來進(jìn)行控制,就能達(dá)到這種效果。
我們可以看一下Apache官網(wǎng)上對(duì)Shiro,它都包含哪些功能。
我們看看它每一個(gè)模塊代表的是什么意思
Authentication:認(rèn)證,有時(shí)也簡(jiǎn)稱為“登錄”,這是一個(gè)證明用戶是他們所說的他們是誰的行為。
Authorization:授權(quán),訪問控制的過程,也就是絕對(duì)“誰”去訪問“什么” 授權(quán)用于回答安全問題,例如“用戶是否允許編輯帳戶”,“該用戶是否允許查看此網(wǎng)頁(yè)”,“該用戶是否可以訪問”到這個(gè)按鈕?“這些都是決定用戶有權(quán)訪問的決定,因此都代表授權(quán)檢查。
Cryptography:密碼術(shù)是通過隱藏信息或?qū)⑵滢D(zhuǎn)換為無意義來保護(hù)信息免受不良訪問的做法,因此沒有其他人可以閱讀它。Shiro專注于密碼學(xué)的兩個(gè)核心要素:使用公鑰或私鑰加密數(shù)據(jù)的密碼,以及對(duì)密碼等數(shù)據(jù)進(jìn)行不可逆轉(zhuǎn)加密的哈希(也稱為消息摘要)。
Shiro Cryptography的主要目標(biāo)是采用傳統(tǒng)上非常復(fù)雜的領(lǐng)域,并在提供強(qiáng)大的密碼學(xué)功能的同時(shí)使其他人輕松實(shí)現(xiàn)。
Session Management:Session會(huì)話,會(huì)話是您的用戶在使用您的應(yīng)用程序時(shí)攜帶一段時(shí)間的數(shù)據(jù)桶。傳統(tǒng)上,會(huì)話專用于Web或EJB環(huán)境。不再!Shiro支持任何應(yīng)用程序環(huán)境的會(huì)話。此外,Shiro還提供許多其他強(qiáng)大功能來幫助您管理會(huì)話。
Web Support:Shiro的web支持的API能夠輕松地幫助保護(hù) Web 應(yīng)用程序。主要就是用來對(duì)Web程序進(jìn)行一個(gè)好的支持的。
Caching:緩存,他是Apache Shiro中的第一層公民,來確保安全操作快速而又高效。
Concurrency:shiro利用它的并發(fā)特性來支持多線程應(yīng)用程序
Testing:測(cè)試支持的存在來幫助你編寫單元測(cè)試和集成測(cè)試,并確保你的能夠如預(yù)期的一樣安全。
“Run As”:其實(shí)這個(gè)就是有是有允許一個(gè)用戶假設(shè)為另外一個(gè)用戶身份的功能,有時(shí)候在管理腳本的時(shí)候很有效果
Remember Me:在會(huì)話中記住用戶的身份,所以他們只需要在強(qiáng)制時(shí)候登錄。
Shiro核心
Shiro其實(shí)是有三大核心組件的,Subject、SecurityManager和Realms。
Subject:Subject實(shí)質(zhì)上是一個(gè)當(dāng)前執(zhí)行用戶的特定的安全“視圖”。鑒于”User”一詞通常意味著一個(gè)人,而一個(gè)Subject可以是一個(gè)人, 但它還可以代表第三方服務(wù),daemon account,cron job,或其他類似的任何東西——基本上是當(dāng)前正與軟件進(jìn)行交互的任何東西。 所有Subject實(shí)例都被綁定到(且這是必須的)一個(gè)SecurityManager上。當(dāng)你與一個(gè)Subject交互時(shí),那些交互作用轉(zhuǎn)化為與SecurityManager交互的特定subject的交互作用。 我們可以把Subject認(rèn)為是一個(gè)門面,SecurityManager才是真正的執(zhí)行者。
SecurityManager:安全管理器,也就是說所有與安全有關(guān)的操作都會(huì)與SecurityManager進(jìn)行交互,而且他管理這Subject,它其實(shí)是Shiro的核心 是Shiro架構(gòu)的心臟。并作為一種“保護(hù)傘”對(duì)象來協(xié)調(diào)內(nèi)部的安全組件共同構(gòu)成一個(gè)對(duì)象圖。
Realms: 域,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶、角色、權(quán)限),就是說SecurityManager要驗(yàn)證用戶身份,那么它需要從Realm獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進(jìn)行驗(yàn)證用戶是否能進(jìn)行操作;可以把Realm看成DataSource,即安全數(shù)據(jù)源。
當(dāng)配置Shiro時(shí),你必須指定至少一個(gè)Realm用來進(jìn)行身份驗(yàn)證和/或授權(quán)。SecurityManager可能配置多個(gè)Realms,但至少有一個(gè)是必須的。
我們可以通過一個(gè)簡(jiǎn)單的登錄來看shiro對(duì)權(quán)限的控制。
我們就通過圖解來理解一下,然后寫個(gè)簡(jiǎn)單的代碼
上圖是對(duì)shiro角色權(quán)限的設(shè)計(jì),
而接下來我們就可以看一下它具體的登錄圖解了
我解釋一下這個(gè)圖。
1、登陸操作 攜帶用戶名密碼給subject,subject調(diào)用自己的登陸方法傳遞用戶名和密碼給權(quán)限管理器,權(quán)限管理器將用戶名密碼傳遞給開發(fā)人員編寫的realm的認(rèn)證方法,realm根據(jù)用戶名到數(shù)據(jù)庫(kù)中查找是否存在該用戶,若存在將認(rèn)證信息存入到session中
/*
* 參數(shù)1:登陸標(biāo)識(shí)
* 參數(shù)2:正確的密碼
* 參數(shù)3:認(rèn)證|授權(quán)器的名字
*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
2、權(quán)限管理器會(huì)自動(dòng)判斷傳遞的密碼與正確密碼是否一致
3、訪問3類資源(頁(yè)面) 過濾器尋找權(quán)限管理器判斷該用戶是否擁有xxx權(quán)限,權(quán)限管理器從session中取出認(rèn)證信息對(duì)象,返回給realm,realm判斷該用戶擁有什么權(quán)限,封裝到授權(quán)信息中返回給權(quán)限管理器,權(quán)限管理器將判斷的結(jié)果返回給過濾器
4、訪問3類資源(xxx添加需要訪問service)(對(duì)于過濾器來說屬于2類資源),在執(zhí)行方法時(shí),會(huì)到達(dá)前置通知(esrvice方法上添加注解@RequiresPermissions(“courier:list”)),權(quán)限通知尋找權(quán)限管理器判斷該用戶是否擁有xxx權(quán)限,權(quán)限管理器從session中取出認(rèn)證信息對(duì)象,返回給realm,realm判斷該用戶擁有什么權(quán)限,封裝到授權(quán)信息中返回給權(quán)限管理器,權(quán)限管理器將判斷的結(jié)果返回給權(quán)限通知
其實(shí)簡(jiǎn)單來說 /userAction_login ———->請(qǐng)求先到達(dá)權(quán)限過濾器shiroFilter,先判斷是幾類資源
登錄屬于一類資源直接放行到————>userActon中(userAction中調(diào)用執(zhí)行subject對(duì)象(使用入口是一個(gè)操作入口對(duì)象,里面有登陸方法,登出方法,獲取當(dāng)前對(duì)象方法)的登陸方法subject.login方法(攜帶著用戶名,密碼)
————>subject對(duì)象調(diào)用 securityManager的login方法 權(quán)限管理器不能判斷用戶和密碼是對(duì)的需要
————>ream認(rèn)證授權(quán)器(開發(fā)人員編寫,判斷用戶名是否存在,擁有什么權(quán)限)————>處理完后把認(rèn)證信息對(duì)象返回給securityManager()如果認(rèn)證信息沒有問題,權(quán)限管理器會(huì)把認(rèn)證信息存入session(證明認(rèn)證登陸過了)
可以自定義一個(gè)Realm;
@Service("MyRealm")
public class MyRealm extends AuthorizingRealm{ //父類接口Realm
@Autowired
private UserDao ud;
@Autowired
private RoleService rs;
@Autowired
private PermissionService ps;
@Override
//授權(quán)
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<Role> roles = rs.findByUser(user);
if(roles != null && roles.size() > 0){
for (Role role : roles) {
info.addRole(role.getKeyword());
}
}
List<Permission> permissions = ps.findByUser(user);
if(permissions != null && permissions.size() > 0) {
for (Permission permission : permissions) {
info.addStringPermission(permission.getKeyword());
}
}
return info;
}
@Override
//認(rèn)證
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken t) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) t;
String username = token.getUsername();
User user = ud.findByUsername(username);
if(user != null){
/*
* 參數(shù)1:登陸標(biāo)識(shí)
* 參數(shù)2:正確的密碼
* 參數(shù)3:認(rèn)證|授權(quán)器的名字
*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
//ActionContext.getContext().getSession().put("loginUser", user);
return info;
} else {
return null;
}
}
}
登陸完以后訪問頁(yè)面資源(頁(yè)面資源屬于三類資源需要權(quán)限),
shiroFilter(已經(jīng)配置了哪些資源是一類哪些資源是三類)
————>訪問權(quán)限管理器,找權(quán)限管理器判斷是否有xxx權(quán)限(權(quán)限管理器本身不能做出判斷),權(quán)限管理器把之前登陸時(shí)保存在session中的認(rèn)證信息取出
交給————>realm判斷(realm中認(rèn)證方法是登陸時(shí)候調(diào)用的),realm查詢數(shù)據(jù)庫(kù)獲得權(quán)限,把權(quán)限信息返還給————>權(quán)限管理器。
權(quán)限管理器根據(jù)realm的授權(quán)信息判斷是否擁有xxx權(quán)限, 判斷后把結(jié)果通知給————>權(quán)限管理器,權(quán)限管理器ShiraFilter 如果沒有權(quán)限跳轉(zhuǎn)到響應(yīng)頁(yè)面。
這其實(shí)就是一個(gè)簡(jiǎn)單的shiro框架的設(shè)計(jì),可能個(gè)人設(shè)計(jì)的有點(diǎn)小毛病,只是一個(gè)測(cè)試,大家自行體會(huì)
總結(jié)
Shiro是一個(gè)功能很齊全的框架,使用起來也很容易,總結(jié)一下 三大核心內(nèi)容:
- Subject
- SecurityManager
- Realms
Shiro 功能強(qiáng)大、且 簡(jiǎn)單、靈活。是Apache 下的項(xiàng)目比較可靠,且不跟任何的框架或者容器綁定,可以獨(dú)立運(yùn)行
所以這個(gè)權(quán)限控制框架,大家理解了么?有想法的咱們可以共同交流一下。






