認(rèn)證就是驗(yàn)證用戶身份的過(guò)程。在認(rèn)證過(guò)程中,用戶需要提交實(shí)體信息(Principals)和憑據(jù)信息(Credentials)以檢驗(yàn)用戶是否合法。最常見的“實(shí)體/憑證”組合便是“用戶名/密碼”組合。
一、Shiro認(rèn)證過(guò)程
1、收集實(shí)體/憑據(jù)信息
-
- UsernamePasswordToken token = new UsernamePasswordToken(username, password);
-
- token.setRememberMe(true);
UsernamePasswordToken支持最常見的用戶名/密碼的認(rèn)證機(jī)制。同時(shí),由于它實(shí)現(xiàn)了RememberMeAuthenticationToken接口,我們可以通過(guò)令牌設(shè)置“記住我”的功能。
但是,“已記住”和“已認(rèn)證”是有區(qū)別的:
已記住的用戶僅僅是非匿名用戶,你可以通過(guò)subject.getPrincipals()獲取用戶信息。但是它并非是完全認(rèn)證通過(guò)的用戶,當(dāng)你訪問(wèn)需要認(rèn)證用戶的功能時(shí),你仍然需要重新提交認(rèn)證信息。
這一區(qū)別可以參考亞馬遜網(wǎng)站,網(wǎng)站會(huì)默認(rèn)記住登錄的用戶,再次訪問(wèn)網(wǎng)站時(shí),對(duì)于非敏感的頁(yè)面功能,頁(yè)面上會(huì)顯示記住的用戶信息,但是當(dāng)你訪問(wèn)網(wǎng)站賬戶信息時(shí)仍然需要再次進(jìn)行登錄認(rèn)證。
2、提交實(shí)體/憑據(jù)信息
- Subject currentUser = SecurityUtils.getSubject();
- currentUser.login(token);
收集了實(shí)體/憑據(jù)信息之后,我們可以通過(guò)SecurityUtils工具類,獲取當(dāng)前的用戶,然后通過(guò)調(diào)用login方法提交認(rèn)證。
3、認(rèn)證處理
- try {
- currentUser.login(token);
- } catch ( UnknownAccountException uae ) { ...
- } catch ( IncorrectCredentialsException ice ) { ...
- } catch ( LockedAccountException lae ) { ...
- } catch ( ExcessiveAttemptsException eae ) { ...
- } ... catch your own ...
- } catch ( AuthenticationException ae ) {
-
- }
如果login方法執(zhí)行完畢且沒有拋出任何異常信息,那么便認(rèn)為用戶認(rèn)證通過(guò)。之后在應(yīng)用程序任意地方調(diào)用SecurityUtils.getSubject() 都可以獲取到當(dāng)前認(rèn)證通過(guò)的用戶實(shí)例,使用subject.isAuthenticated()判斷用戶是否已驗(yàn)證都將返回true.
相反,如果login方法執(zhí)行過(guò)程中拋出異常,那么將認(rèn)為認(rèn)證失敗。Shiro有著豐富的層次鮮明的異常類來(lái)描述認(rèn)證失敗的原因,如代碼示例。
二、登出操作 登出操作可以通過(guò)調(diào)用subject.logout()來(lái)刪除你的登錄信息,如:
當(dāng)執(zhí)行完登出操作后,Session信息將被清空,subject將被視作為匿名用戶。
三、認(rèn)證內(nèi)部處理機(jī)制 以上,是Shiro認(rèn)證在應(yīng)用程序中的處理過(guò)程,下面將詳細(xì)解說(shuō)Shiro認(rèn)證的內(nèi)部處理機(jī)制。
如上圖,我們通過(guò)Shiro架構(gòu)圖的認(rèn)證部分,來(lái)說(shuō)明Shiro認(rèn)證內(nèi)部的處理順序:
1、應(yīng)用程序構(gòu)建了一個(gè)終端用戶認(rèn)證信息的AuthenticationToken 實(shí)例后,調(diào)用Subject.login方法。
2、Sbuject的實(shí)例通常是DelegatingSubject類(或子類)的實(shí)例對(duì)象,在認(rèn)證開始時(shí),會(huì)委托應(yīng)用程序設(shè)置的securityManager實(shí)例調(diào)用securityManager.login(token)方法。
3、SecurityManager接受到token(令牌)信息后會(huì)委托內(nèi)置的Authenticator的實(shí)例(通常都是ModularRealmAuthenticator類的實(shí)例)調(diào)用authenticator.authenticate(token). ModularRealmAuthenticator在認(rèn)證過(guò)程中會(huì)對(duì)設(shè)置的一個(gè)或多個(gè)Realm實(shí)例進(jìn)行適配,它實(shí)際上為Shiro提供了一個(gè)可拔插的認(rèn)證機(jī)制。
4、如果在應(yīng)用程序中配置了多個(gè)Realm,ModularRealmAuthenticator會(huì)根據(jù)配置的AuthenticationStrategy(認(rèn)證策略)來(lái)進(jìn)行多Realm的認(rèn)證過(guò)程。在Realm被調(diào)用后,AuthenticationStrategy將對(duì)每一個(gè)Realm的結(jié)果作出響應(yīng)。
注:如果應(yīng)用程序中僅配置了一個(gè)Realm,Realm將被直接調(diào)用而無(wú)需再配置認(rèn)證策略。
5、判斷每一個(gè)Realm是否支持提交的token,如果支持,Realm將調(diào)用getAuthenticationInfo(token); getAuthenticationInfo 方法就是實(shí)際認(rèn)證處理,我們通過(guò)覆蓋Realm的doGetAuthenticationInfo方法來(lái)編寫我們自定義的認(rèn)證處理。
四、使用多個(gè)Realm的處理機(jī)制: 1、Authenticator 默認(rèn)實(shí)現(xiàn)是ModularRealmAuthenticator,它既支持單一Realm也支持多個(gè)Realm。如果僅配置了一個(gè)Realm,ModularRealmAuthenticator 會(huì)直接調(diào)用該Realm處理認(rèn)證信息,如果配置了多個(gè)Realm,它會(huì)根據(jù)認(rèn)證策略來(lái)適配Realm,找到合適的Realm執(zhí)行認(rèn)證信息。
自定義Authenticator的配置:
- [main]
- ...
- authenticator = com.foo.bar.CustomAuthenticator
- securityManager.authenticator = $authenticator
2、AuthenticationStrategy(認(rèn)證策略) 當(dāng)應(yīng)用程序配置了多個(gè)Realm時(shí),ModularRealmAuthenticator將根據(jù)認(rèn)證策略來(lái)判斷認(rèn)證成功或是失敗。
例如,如果只有一個(gè)Realm驗(yàn)證成功,而其他Realm驗(yàn)證失敗,那么這次認(rèn)證是否成功呢?如果大多數(shù)的Realm驗(yàn)證成功了,認(rèn)證是否就認(rèn)為成功呢?或者,一個(gè)Realm驗(yàn)證成功后,是否還需要判斷其他Realm的結(jié)果?認(rèn)證策略就是根據(jù)應(yīng)用程序的需要對(duì)這些問(wèn)題作出決斷。
認(rèn)證策略是一個(gè)無(wú)狀態(tài)的組件,在認(rèn)證過(guò)程中會(huì)經(jīng)過(guò)4次的調(diào)用:
- 在所有Realm被調(diào)用之前
- 在調(diào)用Realm的getAuthenticationInfo 方法之前
- 在調(diào)用Realm的getAuthenticationInfo 方法之后
- 在所有Realm被調(diào)用之后
認(rèn)證策略的另外一項(xiàng)工作就是聚合所有Realm的結(jié)果信息封裝至一個(gè)AuthenticationInfo實(shí)例中,并將此信息返回,以此作為Subject的身份信息。
Shiro有3中認(rèn)證策略的具體實(shí)現(xiàn):
AtLeastOneSuccessfulStrategy |
只要有一個(gè)(或更多)的Realm驗(yàn)證成功,那么認(rèn)證將被視為成功 |
FirstSuccessfulStrategy |
第一個(gè)Realm驗(yàn)證成功,整體認(rèn)證將被視為成功,且后續(xù)Realm將被忽略 |
AllSuccessfulStrategy |
所有Realm成功,認(rèn)證才視為成功 |
ModularRealmAuthenticator 內(nèi)置的認(rèn)證策略默認(rèn)實(shí)現(xiàn)是AtLeastOneSuccessfulStrategy 方式,因?yàn)檫@種方式也是被廣泛使用的一種認(rèn)證策略。當(dāng)然,你也可以通過(guò)配置文件定義你需要的策略,如:
- [main]
- ...
- authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
- securityManager.authenticator.authenticationStrategy = $authcStrategy
- ...
3、Realm的順序 由剛才提到的認(rèn)證策略,可以看到Realm在ModularRealmAuthenticator 里面的順序?qū)φJ(rèn)證是有影響的。
ModularRealmAuthenticator 會(huì)讀取配置在SecurityManager里的Realm。當(dāng)執(zhí)行認(rèn)證是,它會(huì)遍歷Realm集合,對(duì)所有支持提交的token的Realm調(diào)用getAuthenticationInfo 。
因此,如果Realm的順序?qū)δ闶褂玫恼J(rèn)證策略結(jié)果有影響,那么你應(yīng)該在配置文件中明確定義Realm的順序,如:
- blahRealm = com.company.blah.Realm
- ...
- fooRealm = com.company.foo.Realm
- ...
- barRealm = com.company.another.Realm
-
- securityManager.realms = $fooRealm, $barRealm, $blahRealm
(三)Shiro 授權(quán)
授權(quán)即訪問(wèn)控制,它將判斷用戶在應(yīng)用程序中對(duì)資源是否擁有相應(yīng)的訪問(wèn)權(quán)限。
如,判斷一個(gè)用戶有查看頁(yè)面的權(quán)限,編輯數(shù)據(jù)的權(quán)限,擁有某一按鈕的權(quán)限,以及是否擁有打印的權(quán)限等等。
一、授權(quán)的三要素 授權(quán)有著三個(gè)核心元素:權(quán)限、角色和用戶。
權(quán)限 權(quán)限是Apache Shiro安全機(jī)制最核心的元素。它在應(yīng)用程序中明確聲明了被允許的行為和表現(xiàn)。一個(gè)格式良好好的權(quán)限聲明可以清晰表達(dá)出用戶對(duì)該資源擁有的權(quán)限。
大多數(shù)的資源會(huì)支持典型的CRUD操作(create,read,update,delete),但是任何操作建立在特定的資源上才是有意義的。因此,權(quán)限聲明的根本思想就是建立在資源以及操作上。
而我們通過(guò)權(quán)限聲明僅僅能了解這個(gè)權(quán)限可以在應(yīng)用程序中做些什么,而不能確定誰(shuí)擁有此權(quán)限。
于是,我們就需要在應(yīng)用程序中對(duì)用戶和權(quán)限建立關(guān)聯(lián)。
通常的做法就是將權(quán)限分配給某個(gè)角色,然后將這個(gè)角色關(guān)聯(lián)一個(gè)或多個(gè)用戶。
權(quán)限聲明及粒度 Shiro權(quán)限聲明通常是使用以冒號(hào)分隔的表達(dá)式。就像前文所講,一個(gè)權(quán)限表達(dá)式可以清晰的指定資源類型,允許的操作,可訪問(wèn)的數(shù)據(jù)。同時(shí),Shiro權(quán)限表達(dá)式支持簡(jiǎn)單的通配符,可以更加靈活的進(jìn)行權(quán)限設(shè)置。
下面以實(shí)例來(lái)說(shuō)明權(quán)限表達(dá)式。
可查詢用戶數(shù)據(jù)
User:view
可查詢或編輯用戶數(shù)據(jù)
User:view,edit
可對(duì)用戶數(shù)據(jù)進(jìn)行所有操作
User:* 或 user
可編輯id為123的用戶數(shù)據(jù)
User:edit:123
角色 Shiro支持兩種角色模式:
1、傳統(tǒng)角色:一個(gè)角色代表著一系列的操作,當(dāng)需要對(duì)某一操作進(jìn)行授權(quán)驗(yàn)證時(shí),只需判斷是否是該角色即可。這種角色權(quán)限相對(duì)簡(jiǎn)單、模糊,不利于擴(kuò)展。
2、權(quán)限角色:一個(gè)角色擁有一個(gè)權(quán)限的集合。授權(quán)驗(yàn)證時(shí),需要判斷當(dāng)前角色是否擁有該權(quán)限。這種角色權(quán)限可以對(duì)該角色進(jìn)行詳細(xì)的權(quán)限描述,適合更復(fù)雜的權(quán)限設(shè)計(jì)。
下面將詳細(xì)描述對(duì)兩種角色模式的授權(quán)實(shí)現(xiàn)。
二、授權(quán)實(shí)現(xiàn) Shiro支持三種方式實(shí)現(xiàn)授權(quán)過(guò)程:
- 編碼實(shí)現(xiàn)
- 注解實(shí)現(xiàn)
- JSP Taglig實(shí)現(xiàn)
1、基于編碼的授權(quán)實(shí)現(xiàn) 1.1基于傳統(tǒng)角色授權(quán)實(shí)現(xiàn) 當(dāng)需要驗(yàn)證用戶是否擁有某個(gè)角色時(shí),可以調(diào)用Subject 實(shí)例的hasRole*方法驗(yàn)證。
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.hasRole("administrator")) {
} else {
} 相關(guān)驗(yàn)證方法如下:
Subject方法 |
描述 |
hasRole(String roleName) |
當(dāng)用戶擁有指定角色時(shí),返回true |
hasRoles(List<String> roleNames) |
按照列表順序返回相應(yīng)的一個(gè)boolean值數(shù)組 |
hasAllRoles(Collection<String> roleNames) |
如果用戶擁有所有指定角色時(shí),返回true |
斷言支持 Shiro還支持以斷言的方式進(jìn)行授權(quán)驗(yàn)證。斷言成功,不返回任何值,程序繼續(xù)執(zhí)行;斷言失敗時(shí),將拋出異常信息。使用斷言,可以使我們的代碼更加簡(jiǎn)潔。
- Subject currentUser = SecurityUtils.getSubject();
-
-
- currentUser.checkRole("bankTeller");
- openBankAccount();
斷言的相關(guān)方法:
Subject方法 |
描述 |
checkRole(String roleName) |
斷言用戶是否擁有指定角色 |
checkRoles(Collection<String> roleNames) |
斷言用戶是否擁有所有指定角色 |
checkRoles(String... roleNames) |
對(duì)上一方法的方法重載 |
1.2 基于權(quán)限角色授權(quán)實(shí)現(xiàn) 相比傳統(tǒng)角色模式,基于權(quán)限的角色模式耦合性要更低些,它不會(huì)因角色的改變而對(duì)源代碼進(jìn)行修改,因此,基于權(quán)限的角色模式是更好的訪問(wèn)控制方式。
它的代碼實(shí)現(xiàn)有以下幾種實(shí)現(xiàn)方式:
1、基于權(quán)限對(duì)象的實(shí)現(xiàn) 創(chuàng)建org.apache.shiro.authz.Permission的實(shí)例,將該實(shí)例對(duì)象作為參數(shù)傳遞給Subject.isPermitted()進(jìn)行驗(yàn)證。
- Permission printPermission = new PrinterPermission("laserjet4400n", "print");
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted(printPermission)) {
-
- } else {
-
- }
- Permission printPermission = new PrinterPermission("laserjet4400n", "print");
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted(printPermission)) {
-
- } else {
-
- }
相關(guān)方法如下:
Subject方法 |
描述 |
isPermitted(Permission p) |
Subject擁有制定權(quán)限時(shí),返回treu |
isPermitted(List<Permission> perms) |
返回對(duì)應(yīng)權(quán)限的boolean數(shù)組 |
isPermittedAll(Collection<Permission> perms) |
Subject擁有所有制定權(quán)限時(shí),返回true |
2、 基于字符串的實(shí)現(xiàn) 相比笨重的基于對(duì)象的實(shí)現(xiàn)方式,基于字符串的實(shí)現(xiàn)便顯得更加簡(jiǎn)潔。
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted("printer:print:laserjet4400n")) {
-
- } else {
-
- }
使用冒號(hào)分隔的權(quán)限表達(dá)式是org.apache.shiro.authz.permission.WildcardPermission 默認(rèn)支持的實(shí)現(xiàn)方式。
這里分別代表了 資源類型:操作:資源ID
類似基于對(duì)象的實(shí)現(xiàn)相關(guān)方法,基于字符串的實(shí)現(xiàn)相關(guān)方法:
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms)
基于權(quán)限對(duì)象的斷言實(shí)現(xiàn)
- Subject currentUser = SecurityUtils.getSubject();
-
-
- Permission p = new AccountPermission("open");
- currentUser.checkPermission(p);
- openBankAccount();
基于字符串的斷言實(shí)現(xiàn)
- Subject currentUser = SecurityUtils.getSubject();
-
-
- currentUser.checkPermission("account:open");
- openBankAccount();
斷言實(shí)現(xiàn)的相關(guān)方法
Subject方法 |
說(shuō)明 |
checkPermission(Permission p) |
斷言用戶是否擁有制定權(quán)限 |
checkPermission(String perm) |
斷言用戶是否擁有制定權(quán)限 |
checkPermissions(Collection<Permission> perms) |
斷言用戶是否擁有所有指定權(quán)限 |
checkPermissions(String... perms) |
斷言用戶是否擁有所有指定權(quán)限 |
2、基于注解的授權(quán)實(shí)現(xiàn) Shiro注解支持AspectJ、Spring、Google-Guice等,可根據(jù)應(yīng)用進(jìn)行不同的配置。
相關(guān)的注解:
@ RequiresAuthentication 可以用戶類/屬性/方法,用于表明當(dāng)前用戶需是經(jīng)過(guò)認(rèn)證的用戶。
- @RequiresAuthentication
- public void updateAccount(Account userAccount) {
-
-
- ...
- }
@ RequiresGuest 表明該用戶需為”guest”用戶
@ RequiresPermissions 當(dāng)前用戶需擁有制定權(quán)限
- @RequiresPermissions("account:create")
- public void createAccount(Account account) {
-
-
- ...
- }
@RequiresRoles 當(dāng)前用戶需擁有制定角色
@ RequiresUser 當(dāng)前用戶需為已認(rèn)證用戶或已記住用戶
3、基于JSP TAG的授權(quán)實(shí)現(xiàn) Shiro提供了一套JSP標(biāo)簽庫(kù)來(lái)實(shí)現(xiàn)頁(yè)面級(jí)的授權(quán)控制。
在使用Shiro標(biāo)簽庫(kù)前,首先需要在JSP引入shiro標(biāo)簽:
- <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
下面一一介紹Shiro的標(biāo)簽:
guest標(biāo)簽
驗(yàn)證當(dāng)前用戶是否為“訪客”,即未認(rèn)證(包含未記?。┑挠脩?
- <shiro:guest>
- Hi there! Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today!
- </shiro:guest>
user標(biāo)簽
認(rèn)證通過(guò)或已記住的用戶
- <shiro:user>
- Welcome back John! Not John? Click <a href="login.jsp">here<a> to login.
- </shiro:user>
authenticated標(biāo)簽
已認(rèn)證通過(guò)的用戶。不包含已記住的用戶,這是與user標(biāo)簽的區(qū)別所在。
- <shiro:authenticated>
- <a href="updateAccount.jsp">Update your contact information</a>.
- </shiro:authenticated>
notAuthenticated標(biāo)簽
未認(rèn)證通過(guò)用戶,與authenticated標(biāo)簽相對(duì)應(yīng)。與guest標(biāo)簽的區(qū)別是,該標(biāo)簽包含已記住用戶。
- <shiro:notAuthenticated>
- Please <a href="login.jsp">login</a> in order to update your credit card information.
- </shiro:notAuthenticated>
principal 標(biāo)簽
輸出當(dāng)前用戶信息,通常為登錄賬號(hào)信息
- Hello, <shiro:principal/>, how are you today?
hasRole標(biāo)簽
驗(yàn)證當(dāng)前用戶是否屬于該角色
- <shiro:hasRole name="administrator">
- <a href="admin.jsp">Administer the system</a>
- </shiro:hasRole>
lacksRole標(biāo)簽
與hasRole標(biāo)簽邏輯相反,當(dāng)用戶不屬于該角色時(shí)驗(yàn)證通過(guò)
- <shiro:lacksRole name="administrator">
- Sorry, you are not allowed to administer the system.
- </shiro:lacksRole>
hasAnyRole標(biāo)簽
驗(yàn)證當(dāng)前用戶是否屬于以下任意一個(gè)角色。
- <shiro:hasAnyRoles name="developer, project manager, administrator">
- You are either a developer, project manager, or administrator.
- </shiro:lacksRole>
hasPermission標(biāo)簽
驗(yàn)證當(dāng)前用戶是否擁有制定權(quán)限
- <shiro:hasPermission name="user:create">
- <a href="createUser.jsp">Create a new User</a>
- </shiro:hasPermission>
lacksPermission標(biāo)簽
與hasPermission標(biāo)簽邏輯相反,當(dāng)前用戶沒有制定權(quán)限時(shí),驗(yàn)證通過(guò)
- <shiro:hasPermission name="user:create">
- <a href="createUser.jsp">Create a new User</a>
- </shiro:hasPermission>
三、Shiro授權(quán)的內(nèi)部處理機(jī)制 1、在應(yīng)用程序中調(diào)用授權(quán)驗(yàn)證方法(Subject的isPermitted*或hasRole*等)
2、Sbuject的實(shí)例通常是DelegatingSubject類(或子類)的實(shí)例對(duì)象,在認(rèn)證開始時(shí),會(huì)委托應(yīng)用程序設(shè)置的securityManager實(shí)例調(diào)用相應(yīng)的isPermitted*或hasRole*方法。
3、接下來(lái)SecurityManager會(huì)委托內(nèi)置的Authorizer的實(shí)例(默認(rèn)是ModularRealmAuthorizer 類的實(shí)例,類似認(rèn)證實(shí)例,它同樣支持一個(gè)或多個(gè)Realm實(shí)例認(rèn)證)調(diào)用相應(yīng)的授權(quán)方法。
4、每一個(gè)Realm將檢查是否實(shí)現(xiàn)了相同的 Authorizer 接口。然后,將調(diào)用Reaml自己的相應(yīng)的授權(quán)驗(yàn)證方法。
當(dāng)使用多個(gè)Realm時(shí),不同于認(rèn)證策略處理方式,授權(quán)處理過(guò)程中:
1、當(dāng)調(diào)用Realm出現(xiàn)異常時(shí),將立即拋出異常,結(jié)束授權(quán)驗(yàn)證。
2、只要有一個(gè)Realm驗(yàn)證成功,那么將認(rèn)為授權(quán)成功,立即返回,結(jié)束認(rèn)證。
(四)Realm 實(shí)現(xiàn)
在認(rèn)證、授權(quán)內(nèi)部實(shí)現(xiàn)機(jī)制中都有提到,最終處理都將交給Real進(jìn)行處理。因?yàn)樵赟hiro中,最終是通過(guò)Realm來(lái)獲取應(yīng)用程序中的用戶、角色及權(quán)限信息的。通常情況下,在Realm中會(huì)直接從我們的數(shù)據(jù)源中獲取Shiro需要的驗(yàn)證信息??梢哉f(shuō),Realm是專用于安全框架的DAO.
一、認(rèn)證實(shí)現(xiàn) 正如前文所提到的,Shiro的認(rèn)證過(guò)程最終會(huì)交由Realm執(zhí)行,這時(shí)會(huì)調(diào)用Realm的getAuthenticationInfo(token)方法。
該方法主要執(zhí)行以下操作:
1、檢查提交的進(jìn)行認(rèn)證的令牌信息
2、根據(jù)令牌信息從數(shù)據(jù)源(通常為數(shù)據(jù)庫(kù))中獲取用戶信息
3、對(duì)用戶信息進(jìn)行匹配驗(yàn)證。
4、驗(yàn)證通過(guò)將返回一個(gè)封裝了用戶信息的AuthenticationInfo實(shí)例。
5、驗(yàn)證失敗則拋出AuthenticationException異常信息。
而在我們的應(yīng)用程序中要做的就是自定義一個(gè)Realm類,繼承AuthorizingRealm抽象類,重載doGetAuthenticationInfo (),重寫獲取用戶信息的方法。
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
- UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
- User user = accountManager.findUserByUserName(token.getUsername());
- if (user != null) {
- return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());
- } else {
- return null;
- }
- }
二、授權(quán)實(shí)現(xiàn) 而授權(quán)實(shí)現(xiàn)則與認(rèn)證實(shí)現(xiàn)非常相似,在我們自定義的Realm中,重載doGetAuthorizationInfo()方法,重寫獲取用戶權(quán)限的方法即可。
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- String userName = (String) principals.fromRealm(getName()).iterator().next();
- User user = accountManager.findUserByUserName(userName);
- if (user != null) {
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- for (Group group : user.getGroupList()) {
- info.addStringPermissions(group.getPermissionList());
- }
- return info;
- } else {
- return null;
- }
- }
(五)Shiro 配置說(shuō)明
Apache Shiro的配置主要分為四部分:
- 對(duì)象和屬性的定義與配置
- URL的過(guò)濾器配置
- 靜態(tài)用戶配置
- 靜態(tài)角色配置
其中,由于用戶、角色一般由后臺(tái)進(jìn)行操作的動(dòng)態(tài)數(shù)據(jù),因此Shiro配置一般僅包含前兩項(xiàng)的配置。
Apache Shiro的大多數(shù)組件是基于POJO的,因此我們可以使用POJO兼容的任何配置機(jī)制進(jìn)行配置,例如:Java代碼、Sping XML、YAML、JSON、ini文件等等。下面,以Spring XML的配置方式為例,并且對(duì)其中的一些配置參數(shù)進(jìn)行一些簡(jiǎn)單說(shuō)明。
Shiro對(duì)象的配置: 主要是對(duì)Shiro各個(gè)組件的實(shí)現(xiàn)進(jìn)行定義配置,主要組件在前文已做過(guò)簡(jiǎn)單介紹,這里不再一一說(shuō)明。
- <bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
- <property name="cacheManager" ref="cacheManager"/>
<property name="sessionMode" value="native"/>
<property name="realm" ref="myRealm"/>
<property name="sessionManager" ref="sessionManager"/>
</bean> Shiro過(guò)濾器的配置 Shiro主要是通過(guò)URL過(guò)濾來(lái)進(jìn)行安全管理,這里的配置便是指定具體授權(quán)規(guī)則定義。
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <property name="securityManager" ref="securityManager"/>
- <property name="loginUrl" value="/login.jsp"/>
- <property name="successUrl" value="/home.jsp"/>
- <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
- <property name="filterChainDefinitions">
- <value>
- # some example chain definitions:
- /admin/** = authc, roles[admin]
- /docs/** = authc, perms[document:read]
- /** = authc
- # more URL-to-FilterChain definitions here
- </value>
- </property>
- </bean>
URL過(guò)濾器配置說(shuō)明: Shiro可以通過(guò)配置文件實(shí)現(xiàn)基于URL的授權(quán)驗(yàn)證。FilterChain定義格式:
URL_Ant_Path_Expression = Path_Specific_Filter_Chain
每個(gè)URL配置,表示匹配該URL的應(yīng)用程序請(qǐng)求將由對(duì)應(yīng)的過(guò)濾器進(jìn)行驗(yàn)證。
例如:
[urls]
/index.html = anon
/user/create = anon
/user/** = authc
/admin/** = authc, roles[administrator]
/rest/** = authc, rest
/remoting/rpc/** = authc, perms["remote:invoke"]
URL表達(dá)式說(shuō)明 1、URL目錄是基于HttpServletRequest.getContextPath()此目錄設(shè)置
2、URL可使用通配符,**代表任意子目錄
3、Shiro驗(yàn)證URL時(shí),URL匹配成功便不再繼續(xù)匹配查找。所以要注意配置文件中的URL順序,尤其在使用通配符時(shí)。
Filter Chain定義說(shuō)明 1、一個(gè)URL可以配置多個(gè)Filter,使用逗號(hào)分隔
2、當(dāng)設(shè)置多個(gè)過(guò)濾器時(shí),全部驗(yàn)證通過(guò),才視為通過(guò)
3、部分過(guò)濾器可指定參數(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 |