將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ)簡(jiǎn)介: Shiro 是 Java 世界中新近出現(xiàn)的權(quán)限框架,較之 JAAS 和 Spring Security,Shiro 在保持強(qiáng)大功能的同時(shí),還在簡(jiǎn)單性和靈活性方面擁有巨大優(yōu)勢(shì)。本文介紹了 Shiro 的關(guān)鍵概念和權(quán)限模型,同時(shí)給出了 Shiro 以及 Grails Shiro Plugin 的使用示例。在閱讀本文的過程中,讀者可以充分的體會(huì)到 Shiro 的魅力
前言 Shiro 是 JAVA 世界中新近出現(xiàn)的權(quán)限框架,較之 JAAS 和 Spring Security,Shiro 在保持強(qiáng)大功能的同時(shí),還在簡(jiǎn)單性和靈活性方面擁有巨大優(yōu)勢(shì)。本文就帶領(lǐng)讀者一睹 Shiro 的風(fēng)采。 可能大家早先會(huì)見過 J-security,這個(gè)是 Shiro 的前身。在 2009 年 3 月初之前,這個(gè)安全框架叫做 J-security,由于某些原因,更名為 Shiro(或者 Ki,意為 Fortress),是 Apache 的孵化項(xiàng)目,鑒于本文編寫時(shí) Shiro 的還沒有正式發(fā)布的版本,本文使用的是 Jsecurity 的穩(wěn)定版本 0.9,本文中 Shiro 等同于 Jsecurity。 本文將涉及 Shiro 的整體框架、安全模型、關(guān)鍵概念類,同時(shí)給出了 Shiro 以及 Grails Shiro Plugin 的使用示例,可以下載文中使用的源代碼。 本文代碼的開發(fā)環(huán)境: - Jsecurity 0.9
- Grails 1.2.0
- Grails Shiro Plugin 1.0.1
- SpringSource Tool Suite 2.3
Shiro 是一個(gè)強(qiáng)大而靈活的開源安全框架,能夠非常清晰的處理認(rèn)證、授權(quán)、管理會(huì)話以及密碼加密。如下是它所具有的特點(diǎn): - 易于理解的 Java Security API;
- 簡(jiǎn)單的身份認(rèn)證(登錄),支持多種數(shù)據(jù)源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
- 對(duì)角色的簡(jiǎn)單的簽權(quán)(訪問控制),支持細(xì)粒度的簽權(quán);
- 支持一級(jí)緩存,以提升應(yīng)用程序的性能;
- 內(nèi)置的基于 POJO 企業(yè)會(huì)話管理,適用于 Web 以及非 Web 的環(huán)境;
- 異構(gòu)客戶端會(huì)話訪問;
- 非常簡(jiǎn)單的加密 API;
- 不跟任何的框架或者容器捆綁,可以獨(dú)立運(yùn)行。
目前還有其他出現(xiàn)較早的安全框架,比如 JAAS,Spring Security。 JAAS —面世的時(shí)間最早,但是鑒于其在使用上有很大的限制,很少有人真正的使用它??梢哉f它不是一個(gè)好的應(yīng)用程序級(jí)別的安全框架; Spring Security —目前是 Java 安全框架領(lǐng)域當(dāng)之無愧的老大,已經(jīng)非常成熟了;如果使用 Spring 框架,可以首選 Spring Security,但是對(duì)于單應(yīng)用來說,Shiro 更顯簡(jiǎn)單方便。 下面就開始我們的 Shiro 之旅吧! 回頁首 整體架構(gòu) 首先,我們來看看的 Shiro 的整體架構(gòu),見下圖: 圖 1. 整體架構(gòu)
從上圖可以看出,Shiro 主要有四個(gè)組件: - SecurityManager
典型的 Facade,Shiro 通過它對(duì)外提供安全管理的各種服務(wù)。 - Authenticator
對(duì)“Who are you ?”進(jìn)行核實(shí)。通常涉及用戶名和密碼。 這個(gè)組件負(fù)責(zé)收集 principals 和 credentials,并將它們提交給應(yīng)用系統(tǒng)。如果提交的 credentials 跟應(yīng)用系統(tǒng)中提供的 credentials 吻合,就能夠繼續(xù)訪問,否則需要重新提交 principals 和 credentials,或者直接終止訪問。 - Authorizer
身份份驗(yàn)證通過后,由這個(gè)組件對(duì)登錄人員進(jìn)行訪問控制的篩查,比如“who can do what”, 或者“who can do which actions”。Shiro 采用“基于 Realm”的方法,即用戶(又稱 Subject)、用戶組、角色和 permission 的聚合體。 - Session Manager
這個(gè)組件保證了異構(gòu)客戶端的訪問,配置簡(jiǎn)單。它是基于 POJO/J2SE 的,不跟任何的客戶端或者協(xié)議綁定。
Shiro 的認(rèn)證和簽權(quán)可以通過 JDBC、LDAP 或者 Active Directory 來訪問數(shù)據(jù)庫、目錄服務(wù)器或者 Active Directory 中的人員以及認(rèn)證 / 簽權(quán)信息。SessionManager 通過會(huì)話 DAO 可以將會(huì)話保存在 cache 中,或者固化到數(shù)據(jù)庫或文件系統(tǒng)中。 回頁首 安全模型 從 Shiro 的框架圖,已經(jīng)能夠體會(huì)到這個(gè)工具的簡(jiǎn)單了。下面讓我們來看看 Shiro 是如何工作的。先了解一下它的安全模型吧!見下圖: 圖 2. 安全模型
上圖中,涉及了 Shiro 的五個(gè)概念: - Subject 是安全領(lǐng)域術(shù)語,除了代表人,它還可以是應(yīng)用。在單應(yīng)用中,可將其視為 User 的同義詞。
- Principal 是 Subject 的標(biāo)識(shí),一般情況下是唯一標(biāo)識(shí),比如用戶名。
- Role 和 Permission 分別代表了不同粒度的權(quán)限,從上圖中可以看出 Role 的粒度更大些,Permission 代表了系統(tǒng)的原子權(quán)限,比如數(shù)據(jù)的修改、刪除權(quán)限。對(duì)于簡(jiǎn)單的權(quán)限應(yīng)用,可以不需要 Permission。
- Realm 是一個(gè)執(zhí)行者,負(fù)責(zé)真正的認(rèn)證和鑒權(quán)。
實(shí)現(xiàn)應(yīng)用的安全模塊的關(guān)鍵在于:定義合適的 role 和 permission,這就需要遵循如下原則: - role 沒有實(shí)質(zhì)內(nèi)容,只是代表一組 permission,目的是為了管理的方便,一般都是動(dòng)態(tài)定義;
- permission 一般都是預(yù)先定義好的,不允許動(dòng)態(tài)改變,除非源代碼改動(dòng),它才會(huì)變化,它是整個(gè)安全模塊的基礎(chǔ);
- 要使 permission 也能動(dòng)態(tài)定義,并非不可能,但是這將使鑒權(quán)非常復(fù)雜,甚至可能導(dǎo)致鑒權(quán)語句遍布整個(gè)程序,得不償失;
- 當(dāng)然有一個(gè)例外:如果知道 permission 動(dòng)態(tài)定義的規(guī)則和鑒權(quán)規(guī)則,如 Grails 的 fileter 中“${controllerName}:${actionName}:${params.id}”也可實(shí)現(xiàn) permission 的動(dòng)態(tài)定義
回頁首 關(guān)鍵概念類 理解 Shiro 的架構(gòu)和安全模型了,我們來看看更具體些的內(nèi)容。下圖顯示了 Shiro 中的關(guān)鍵概念類(參考資料 -- JSecurity Mini Guide)。 圖 3. 關(guān)鍵類
AuthenticationToken 和 AuthenticationInfo 前者在認(rèn)證前使用,描述認(rèn)證所需的信息,最常用的就是 username 和 password 對(duì);后者在認(rèn)證后使用,內(nèi)容同前,但是表示已經(jīng)經(jīng)過認(rèn)證的信息。 RememberMe 代表的是一種可能狀態(tài),并不表示該 Subject 已經(jīng)經(jīng)過了認(rèn)證。對(duì)于一些普通的操作,這種可能狀態(tài)并無大礙,但一旦涉及安全敏感的操作,必須經(jīng)過認(rèn)證。 Credentials 和 CredentialsMatcher Credentials 是 Subject 的證書,在認(rèn)證時(shí)使用,最常用的就是 password。在通常情況下,為了安全起見,Subject 的 credentials 都需要加密保存,于是 CredentialsMatcher 的作用就體現(xiàn)出來了,見下圖: 圖 4. CredentialsMatcher 的作用
這里 CredentialsMatcher 需要將加密后的證書跟用戶登錄時(shí)提供的證書進(jìn)行比對(duì),完成認(rèn)證的過程。 PAM= Pluggable Authentication Modules 在有多個(gè) Realm 的時(shí)候使用。由認(rèn)證策略決定認(rèn)證結(jié)果,即 PAM= Relams + 認(rèn)證策略。一般的策略有 3 種:AllSuccessful、AtLeastOneSuccessful 和 FirstSuccessful。 AuthorizationInfo 可以看成是 Role + Permission 的組合體。 PermissionResolver 和 Permission 它們之間的關(guān)系如下: 圖 5. PermissionResolver 和 Permission 的關(guān)系
在 Shiro 中,權(quán)限被轉(zhuǎn)化為一種字符串描述(字符串分級(jí)表示,稱之為 WildcardPermission),從而將權(quán)限轉(zhuǎn)化為類似于對(duì)象 equals 的操作(Shiro 中的 implies 方法)。 內(nèi)置的權(quán)限有 2 個(gè): - AllPermission,總是返回 true
- WildcardPermission,權(quán)限字符串的表示方式。
這里重點(diǎn)聲明一下。WildcardPermission 是 Shiro 的精妙之處,我們可以將權(quán)限表示成字符串,這樣對(duì)權(quán)限的控制可以不拘泥于物理存儲(chǔ),比如對(duì) messagge 類具有修改和刪除權(quán)限可以標(biāo)識(shí)為:message:update,delete:*,其中‘ * ’表示所有;第一級(jí)分隔符為‘ : ’;第二級(jí)分隔符為‘ , ’,而對(duì)于權(quán)限字符串的解釋完全可以由應(yīng)用自己來定。 如果要比較權(quán)限字符串,可以使用 permission1.implies(permission2),它分別比較對(duì)應(yīng)位置的字符串,在如下情況中,結(jié)果會(huì)返回 true: - permission1 中的子串有 * 或 permission1 子串 ==permission2 子串;
- permission1 無子串,permission2 有;
- permission1 有子串,permission2 無,permission1 的所有子串都是 *。
總的說來,Shiro 中的 Permission 需要注意如下內(nèi)容: - 權(quán)限的比較實(shí)際是字符串的比較,只不過是考慮到了字符串的分級(jí)
- 字符串的分級(jí)劃分完全由使用者自己決定,Shiro 的慣例是 3 級(jí):資源 : 操作 : 實(shí)例。
- 字符串的使用必須一致,分隔符之間不要有空格,避免無意間引入的不一致。如:定義使用“file : create, update : 1”,而驗(yàn)證使用“file : update”,那么分解之后一個(gè)是“ update ”,一個(gè)是“ update”,因空格而引起不等。
Realm 這是一個(gè)實(shí)際訪問安全實(shí)體的組件,一般是應(yīng)用相關(guān)的,跟數(shù)據(jù)源的關(guān)系是 1-1。它負(fù)責(zé)完成認(rèn)證和鑒權(quán),getAuthenticationInfo 代表了 login 的嘗試,鑒權(quán)方法則由 Authorizer 繼承而來。此處也體現(xiàn)了 Shiro 代碼的另一個(gè)特點(diǎn),通過繼承來擴(kuò)充功能。以常用的 JdbcRealm 為例,其繼承鏈如下: 圖 6. JdbcRealm 的繼承鏈
Session 它關(guān)聯(lián)一個(gè) Subject 的上下文,其作用類似于在 HttpSession 中保存用戶標(biāo)識(shí),session 一旦過期,則重新登錄。Shiro 的 Session 是獨(dú)立的,其目的是做到環(huán)境無關(guān)性。為了利用 Web 環(huán)境中,Shiro 實(shí)現(xiàn)了一個(gè)直接使用 HttpSession 的 WebSession。 SecurityManager 這是一個(gè) Fa?ade 接口,=Authenticator + Authorizer + SessionFactory。在整體框架圖中已經(jīng)看到了它在 Shiro 中所處的位置。其特點(diǎn)同 Realm,一樣是使用繼承不斷地?cái)U(kuò)充功能。對(duì)于 Web 應(yīng)用一般使用 DefaultWebSecurityManager。 Filter 在 Web 環(huán)境下使用 filter 進(jìn)行認(rèn)證和權(quán)限檢查是毋庸置疑的,而 Shiro 的特點(diǎn)則在于由一個(gè)主 Filter 將一群子 filter 串起來: 圖 7. Filter 的作用
在實(shí)際使用時(shí),須注意: - web.xml 中只需配置 JSecurityFilter。對(duì)于 Spring 應(yīng)用,則使用 SpringJSecurityFilter;
- 子 filter 作為主 filter 的配置參數(shù)值出現(xiàn),特點(diǎn)是:順序相關(guān)
- 對(duì)于多個(gè) URL,驗(yàn)證順序是由上至下,類似 Exception 的匹配。因此,使用順序應(yīng)該是由細(xì)到粗。
- 對(duì)于同一 URL,子 filter 的驗(yàn)證順序是從左至右的 AND 操作。
- 如果配置值中含有分隔符,如 Permission,就需要使用引號(hào)來轉(zhuǎn)義。
Subject subject 代表了一個(gè)用戶的狀態(tài)和操作,它提供了所有安全相關(guān)的操作,包括認(rèn)證和簽權(quán)??梢詫⑵湟暈榱硪环N形式的 Fa?ade。缺省實(shí)現(xiàn)是將這些操作委派給其內(nèi)部包含的 SecurityManager。 Configuration configuration 負(fù)責(zé)將所有這些組件串起來,最終創(chuàng)建 SecurityManager。在 Shiro 中,缺省格式是 ini。整個(gè)配置關(guān)系如下圖: 圖 8. 配置關(guān)系
其中: - JSecurityFilter 創(chuàng)建 Configuration 實(shí)例,并將 ini 參數(shù)值傳給 Configuation。在 Spring 環(huán)境中,分別使用 SpringJSecurityFilter 和 SpringIniWebConfiguration。
- Configuration 實(shí)際就是 SecurityManager 的 Factroy,對(duì) SpringIniWebConfiguration 而言,它需要知道 SecurityManager 的 BeanName,該值由 SpringJSecurityFilter 的初始化參數(shù)“securityManagerBeanName”值決定。即 SpringJSecurityFilter,實(shí)際有兩個(gè)初始化參數(shù):
- config,是 ini 配置文件內(nèi)容
- securityManagerBeanName,是 SecurityManager 的 BeanName
SecurityUtils 這是 Shiro 中最重要的工具類,由它可以方便地獲得 Subject 和 SecurityManager。 雜項(xiàng) - AOP,提供 AOP 方面的支持,實(shí)現(xiàn)對(duì)某個(gè)類某個(gè)方法的攔截,從而使權(quán)限控制延伸至類的方法。
- Cache,提供緩存支持
- Codec,提供編碼方面的支持
- Crypto,提供加密支持
- IO,從多個(gè)資源位置讀寫原始數(shù)據(jù)
- JNDI,提供 jndi 支持
- util,工具類支持
- 標(biāo)簽類,用于 Web 頁面
回頁首 典型使用 對(duì) Shiro 有了一個(gè)感官認(rèn)識(shí)后,下面我們就親自動(dòng)手試試這個(gè)框架吧!下面給大家舉了兩個(gè)使用案例。 在開始案例的學(xué)習(xí)之前,先作好準(zhǔn)備工作 -- 獲得 Shiro 相關(guān)的 jar 包,獲取途徑有兩種: - 直接到 J-security 的網(wǎng)站上 下載,本文用到的就是這個(gè);
- 由于 Shiro 目前是 Apache 的孵化項(xiàng)目,還沒有發(fā)布正式的版本,但是我們可以到 Subversion 上下載代碼,之后使用 Maven 構(gòu)建
mkdir shiro cd shiro svn co https://svn./repos/asf/incubator/shiro/trunk/ mvn install
之后會(huì)得到 shiro-all-1.0-incubating-SNAPSHOT.jar,就可以使用 Shiro 了。 示例一:讓 Shiro 為你的應(yīng)用服務(wù) 這個(gè)案例中,我們使用 Grails 向大家講述 Shiro 的使用。我們要實(shí)現(xiàn)如下功能: - 用戶登錄后方可進(jìn)入系統(tǒng);
- 假定一個(gè) message 的安全內(nèi)容,用戶可以創(chuàng)建 message 的內(nèi)容,但是如果需要修改 / 刪除 message 的內(nèi)容就必須具有相應(yīng)的權(quán)限;
- Admin 具有所有的權(quán)限;
- message 的權(quán)限跟角色關(guān)聯(lián)。
示例程序執(zhí)行的流程如下: 圖 9 程序執(zhí)行的流程
從上圖中可以看到,任何人要訪問應(yīng)用中受保護(hù)的 URL,首先要通過 Filter 檢查用戶是否經(jīng)過認(rèn)證;對(duì)于沒有認(rèn)證的用戶會(huì)將訪問定向到登錄頁面;對(duì)于已經(jīng)認(rèn)證的用戶,會(huì)對(duì)用戶進(jìn)行鑒權(quán),這個(gè)用戶是否具有訪問其所提交的 URL 的權(quán)限;而管理員可以給角色授權(quán)。 好了,開始程序的編寫啦! 創(chuàng)建安全領(lǐng)域類 最常見的就是 User、Role 和 Permission,見清單 1。 清單 1. User/Role/Permission 的 Domain class
class User {
String username
String password
static hasMany= [roles: Role]
static belongsTo= Role
……
}
class Role {
String rolename
static hasMany= [users: User, permissions: Permission]
……
}
class Permission {
String permission
static hasMany= [roles: Role]
static belongsTo= Role
……
}
|
這里使用了最簡(jiǎn)單的情形,即權(quán)限傳遞結(jié)構(gòu)為:Permission -> Role -> User。通常情況下,Permission 也可以分配給單個(gè) User。 創(chuàng)建一個(gè)安全實(shí)體 實(shí)體名為 message,見清單 2。只有經(jīng)過授權(quán)的用戶才能對(duì)這個(gè)實(shí)體進(jìn)行修改和刪除。 清單 2. message 的 Domain class
class Message {
String details
User user
static constraints = {
}
}
|
配置 web.xml 清單 3. 在 web.xml 加入 SecurityFilter 的內(nèi)容:
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>
org.jsecurity.spring.SpringJSecurityFilter
</filter-class>
<init-param>
<param-name>securityManagerBeanName</param-name>
<param-value>jsecSecurityManager</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
|
這里需要注意: - 這個(gè) Filter 應(yīng)該在 Grails 的 web.xml 中所有缺省的 Filter 最后;
- url-pattern 不要使用“/**”,因?yàn)檫@樣會(huì)造成登錄頁的 css 和圖片無法訪問。解決辦法,可以通過遵循“只能通過 Controller/Action 訪問”這個(gè)規(guī)則,并使用 Grails 的 Filter 機(jī)制,可以保證所有安全 URL 不被非法訪問。
創(chuàng)建 realm 清單 4. conf/spring/resources.groovy
beans = {
credentialMatcher(
org.jsecurity.authc.credential.Sha1CredentialsMatcher) {
storedCredentialsHexEncoded = true
}
permissionResolver(
org.jsecurity.authz.permission.WildcardPermissionResolver)
realm(org.jsecurity.realm.jdbc.JdbcRealm){
permissionResolver = ref("permissionResolver")
dataSource = ref("dataSource")
permissionsLookupEnabled= true
permissionsQuery= "select permission from
permission, role_permissions, role where
permission.id= permission_id and role_id= role.id and rolename= ?"
userRolesQuery= "select rolename from role, role_users, user
where role.id=role_id and user_id= user.id and username=?"
authenticationQuery= "select password from user where username=?"
}
jsecSecurityManager(
org.jsecurity.web.DefaultWebSecurityManager) {
bean ->bean.destroyMethod = "destroy"
realms = [ ref("realm") ]
}
}
|
這里使用了 JdbcRealm,同時(shí)根據(jù)應(yīng)用情況修改了相應(yīng)的 SQL。如果允許 Permission 直 接分配給 User,即 Permission 和 User 之間是多對(duì)多關(guān)系,那么 permissionsQuery 應(yīng)該使用 union,即“role 相關(guān) permission union user 相關(guān) permission”。對(duì)于 User 有多個(gè) Role 的情況,JdbcRealm 會(huì)循環(huán)得出總的結(jié)果。 安全守護(hù)神:SecurityFilters 下面就是我們的安全守護(hù)神:SecurityFilters,這里遵循 Grails 的 Filter 語法。見清單 5。 清單 5. SecurityFilters
import org.jsecurity.SecurityUtils
class SecurityFilters {
def filters = {
authc(controller:'*', action:'*', ) {
before = {
if(controllerName!='auth'){
def subject = SecurityUtils.subject
if (!subject.authenticated) {
redirect(
controller: 'auth',
action: 'login',
params: [
targetUri: request.forwardURI - request.contextPath
])
return false
}
}
}
}
admin(controller: 'user|role|permission', action: '*'){
before = {
def subject= SecurityUtils.subject
if(!subject.hasRole('admin')){
redirect(controller: 'auth', action: 'unauthorized')
return false
}
}
}
editmessage(controller: 'message', action: 'update|delete'){
before = {
def subject= SecurityUtils.subject
if(!subject.isPermitted(
"${controllerName}:${actionName}:${params.id}")){
redirect(controller: 'auth', action: 'unauthorized')
return false
}
}
}
}
}
|
代碼中 : - authc 表示的是所有用戶對(duì)應(yīng)用系統(tǒng)的任何訪問都要經(jīng)過 auth 認(rèn)證,對(duì)于沒有認(rèn)證的用戶的訪問會(huì)重定向到登錄界面;
- admin 表示的是屬于 admin 角色的用戶對(duì) user/role/permission 具有所有權(quán)限,對(duì)于非 admin 角色的用戶會(huì)對(duì)其提示沒有 user/role/permission 的訪問權(quán)限;
- editmessage 表示當(dāng)用戶對(duì) message 進(jìn)行修改或者刪除的時(shí)候?qū)ζ溥M(jìn)行鑒權(quán),只有具有 message 的 update/delete 權(quán)限的用戶才能對(duì) message 進(jìn)行修改。
在上述代碼中還可以看出,我們通??梢杂?SecurityUtils 為出發(fā)點(diǎn)獲得 Subject 和 SecurityManager。需要注意的是,認(rèn)證的 Filter(authc)要打頭陣。 認(rèn)證的代碼 清單 6. 認(rèn)證代碼
def signIn = {
// 創(chuàng)建 AuthenticationToken
def authToken = new UsernamePasswordToken(
params.username,
params.password)
if (params.rememberMe) {
authToken.rememberMe = true
}
try{
// 使用 SecurityManager 的 login 登錄
this.jsecSecurityManager.login(authToken)
flash.message = message(code: "$params.username")
def targetUri = params.targetUri ?: "/"
log.info "Redirecting to '${targetUri}'."
redirect(uri: targetUri)
}
catch (AuthenticationException ex){
// 如果出現(xiàn)異常,顯示出錯(cuò)信息
log.info "Authentication failure for user '${params.username}'."
flash.message = message(code: "login.failed")
def m = [ username: params.username ]
if (params.rememberMe) {
m['rememberMe'] = true
}
if (params.targetUri) {
m['targetUri'] = params.targetUri
}
redirect(action: 'login', params: m)
}
}
|
授權(quán)部分很簡(jiǎn)單,即對(duì)安全實(shí)體進(jìn)行 CRUD。其中 Permission 的權(quán)限字符串根據(jù)實(shí)際情況形成,在本例中: - 如果對(duì)所有 message 具有修改權(quán)限,權(quán)限字符串可以為:message:delete,update:*;
- 如果針對(duì)某一個(gè) message 具有修改權(quán)限,權(quán)限字符串可以為:message:update,delete:messageid。
示例二:使用 Shiro Plugin 快速構(gòu)建安全模塊 在示例一中,所有的代碼都是自己手動(dòng)寫的,這就對(duì)初學(xué)者要求有些高了。但可喜的是 Grails 社區(qū)有了 Shiro 的 plugin,讓我們的工作變得非常簡(jiǎn)單。同樣示例一的功能,看看 plugin 能夠給我們帶來什么樣的驚喜? 使用步驟如下: - 安裝 Shiro Plugin,在你的 grails 項(xiàng)目中運(yùn)行:
grails install-plugin shiro ,會(huì)創(chuàng)建 grails-app/realms 目錄,并提供如下新的 Grails 命令:grails create-auth-controller ,創(chuàng)建 AuthController 以及登錄窗口,Controller 提供了登錄、登出和權(quán)限驗(yàn)證失敗處理等 Action。grails create-db-realm ,創(chuàng)建一個(gè)訪問數(shù)據(jù)庫的 Realmgrails create-ldap-realm ,創(chuàng)建一個(gè)訪問 ldap 的 Realmgrails create-wildcard-realm ,創(chuàng)建一個(gè)訪問數(shù)據(jù)庫的 Realm,但使用的是 Shiro 的 WildcardPermission。grails quick-start ,它是 create-auth-controller 和 create-db-realm 的集合體,是 Shiro 的快速入門,在接下來的內(nèi)容中將詳細(xì)介紹它的使用。
- 下面進(jìn)行 Shiro 快速入門。在 grails 項(xiàng)目的目錄下執(zhí)行:
grails quick-start ,這個(gè)命令會(huì)創(chuàng)建如下內(nèi)容:- 在 grails-app/realms 下創(chuàng)建 ShiroDbRealm,這個(gè) realm 是訪問控制的信息庫,用來決定一個(gè)用戶有權(quán)訪問哪些內(nèi)容;
- 在 grails-app/domain 創(chuàng)建 ShiroRole.groovy 和 ShiroUser.groovy;
- 在 grails-app/controllers 下創(chuàng)建 AuthController,這里提供了登錄和退出的 action;
- 在 grails-app/views 下創(chuàng)建 auth 文件夾以及 login.gsp;
- 在 grails-app/conf/ 下創(chuàng)建 SecurityFilters.groovy,這里管理著對(duì)所有 controller 和 action 的訪問控制。
- 啟動(dòng)程序,當(dāng)訪問 controller 的時(shí)候,頁面就會(huì)重定向到登錄頁面。但是這個(gè)時(shí)候是無法登錄,因?yàn)槲覀儧]有添加用戶。
- 進(jìn)入到 grails-app/conf/ 修改 BootStrap.groovy,
清單 7. 修改后的 BootStrap.groovy
def init = { servletContext ->
def user = new User(username: "user",
passwordHash: new Sha1Hash("user").toHex())
user.save()
def role= new Role(name:"admin").addToUsers(user).save()
}
|
重新啟動(dòng)程序,就能使用 user/user 登陸了。 - 在 plugin 缺省創(chuàng)建的 SecurityFilters 中使用了對(duì)所有 URL 進(jìn)行 before 攔截,但是我們根據(jù)實(shí)際情況進(jìn)行修改,例如我們要示例一的 filter 內(nèi)容,就可以做如下更改:
清單 8. 更改后的 SecurityFilters
auth(controller: "*", action: "*"){
before={
accessControl{true}
}
}
management(controller: "user|role|permission", action: "*"){
before={
accessControl{role("admin")}
}
}
message(controller: "message", action: "delete|update"){
before={
accessControl{
permission("message:${actionName}:${params.id}")
}
}
}
|
看到這里,讀者可能已經(jīng)注意到了,這里的代碼比示例一中的 Filter 的代碼簡(jiǎn)單的許多。對(duì),Shiro 插件已經(jīng)將示例一中的類似代碼封裝到 ShiroGrailsPlugin.groovy 中了,我們使用的時(shí)候只需要: 調(diào)用 accessControl 方法,該方法的參數(shù)可以是 Map、Closure 或者 Map+Closure; 使用 role( …… ),驗(yàn)證訪問對(duì)象是否具有相應(yīng)的角色; 使用 permission( …… ),驗(yàn)證訪問對(duì)象是否具有相應(yīng)的 Permission。 - 授權(quán)部分內(nèi)容參見上一示例,這里不做冗述。
- Shiro Plugin 中除了為我們提供了上述方便之外,還提供了一些常用的 taglib 來增強(qiáng)用戶界面 , 這些 taglib 都在 pluginPath/grails-app/taglib /ShiroTagLib.groovy 中定義,我們可以直接在 gsp 中使用它們。
比如,只有具有修改權(quán)限的用戶才能夠看到一些修改類的按鈕顯示,可以這樣寫:
清單 9. Taglib 的使用
<shiro:hasPermission permission="message:update,delete">
<span class="button">
<g:actionSubmit class="edit" value="Edit" />
</span>
<span class="button">
<g:actionSubmit class="delete"
onclick="return confirm('Are you sure?');"
value="Delete" />
</span>
</shiro:hasPermission>
|
如下是經(jīng)常使用到的 Tag: - principal,輸出當(dāng)前用戶的標(biāo)識(shí)
- hasRole,判斷當(dāng)前用戶是否屬于給定的角色,參數(shù):name
- hasPermission, 判斷當(dāng)前用戶是否具有指定的權(quán)限,參數(shù):type,action 或者 permission
- isLoggedIn,判斷當(dāng)前用戶是否已經(jīng)登錄
- hasAnyRole,判斷當(dāng)前用戶是否屬于給定的某個(gè)角色,參數(shù):in
更多的 Tag 請(qǐng)參考 Shiro Plugin 的 gapi 的文檔。 如果您已經(jīng)非常了解了 Shiro,可以采用示例一的方式自己寫所有的代碼,但是對(duì)于初學(xué)者,作者還建議使用 Shiro plugin,這個(gè)插件幫助我們生成了安全相關(guān)的基本內(nèi)容,以及提供了方便的 Tag。 回頁首 總結(jié) 讀到這里,是不是覺得 Shiro 真的不錯(cuò)?! 這里給大家舉的示例只是拋磚引玉。Shiro 真正的精髓還需要在項(xiàng)目中慢慢的體會(huì)。本文是引領(lǐng)我們走向 Shiro 的第一步。在這里要感謝胡鍵對(duì)本文編寫的大力支持。 源代碼中,shiroApp 是示例一的源代碼,ShiroPlugin 是示例二的源代碼。
回頁首 下載 描述 | 名字 | 大小 | 下載方法 |
---|
樣例代碼 | shirossc.rar | 2M | HTTP |
關(guān)于下載方法的信息
參考資料 關(guān)于作者 胡偉紅,西安交通大學(xué)碩士,從事軟件開發(fā),曾在 IBM 任職,其間負(fù)責(zé) Websphere 產(chǎn)品技術(shù)支持。時(shí)任 WebsphereChina 專家坐診版主,關(guān)注領(lǐng)域:Groovy/Grails、JBPM、Drools,網(wǎng)站 www.groovyq.net 合伙創(chuàng)辦人,聯(lián)系方式 QQ:27532236,MSN:xiyuan76@hotmail.com。 建議
|