一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ)

 CevenCheng 2011-04-20

胡 偉紅, 自由職業(yè)者

簡(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):

  1. 易于理解的 Java Security API;
  2. 簡(jiǎn)單的身份認(rèn)證(登錄),支持多種數(shù)據(jù)源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
  3. 對(duì)角色的簡(jiǎn)單的簽權(quán)(訪問控制),支持細(xì)粒度的簽權(quán);
  4. 支持一級(jí)緩存,以提升應(yīng)用程序的性能;
  5. 內(nèi)置的基于 POJO 企業(yè)會(huì)話管理,適用于 Web 以及非 Web 的環(huán)境;
  6. 異構(gòu)客戶端會(huì)話訪問;
  7. 非常簡(jiǎn)單的加密 API;
  8. 不跟任何的框架或者容器捆綁,可以獨(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è)組件:

  1. SecurityManager

    典型的 Facade,Shiro 通過它對(duì)外提供安全管理的各種服務(wù)。

  2. 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,或者直接終止訪問。

  3. Authorizer

    身份份驗(yàn)證通過后,由這個(gè)組件對(duì)登錄人員進(jìn)行訪問控制的篩查,比如“who can do what”, 或者“who can do which actions”。Shiro 采用“基于 Realm”的方法,即用戶(又稱 Subject)、用戶組、角色和 permission 的聚合體。

  4. 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,這就需要遵循如下原則:

  1. role 沒有實(shí)質(zhì)內(nèi)容,只是代表一組 permission,目的是為了管理的方便,一般都是動(dòng)態(tài)定義;
  2. permission 一般都是預(yù)先定義好的,不允許動(dòng)態(tài)改變,除非源代碼改動(dòng),它才會(huì)變化,它是整個(gè)安全模塊的基礎(chǔ);
  3. 要使 permission 也能動(dòng)態(tài)定義,并非不可能,但是這將使鑒權(quán)非常復(fù)雜,甚至可能導(dǎo)致鑒權(quán)語句遍布整個(gè)程序,得不償失;
  4. 當(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)容:

  1. 權(quán)限的比較實(shí)際是字符串的比較,只不過是考慮到了字符串的分級(jí)
  2. 字符串的分級(jí)劃分完全由使用者自己決定,Shiro 的慣例是 3 級(jí):資源 : 操作 : 實(shí)例。
  3. 字符串的使用必須一致,分隔符之間不要有空格,避免無意間引入的不一致。如:定義使用“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í),須注意:

  1. web.xml 中只需配置 JSecurityFilter。對(duì)于 Spring 應(yīng)用,則使用 SpringJSecurityFilter;
  2. 子 filter 作為主 filter 的配置參數(shù)值出現(xiàn),特點(diǎn)是:順序相關(guān)
      • 對(duì)于多個(gè) URL,驗(yàn)證順序是由上至下,類似 Exception 的匹配。因此,使用順序應(yīng)該是由細(xì)到粗。
      • 對(duì)于同一 URL,子 filter 的驗(yàn)證順序是從左至右的 AND 操作。
  3. 如果配置值中含有分隔符,如 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 包,獲取途徑有兩種:

  1. 直接到 J-security 的網(wǎng)站上 下載,本文用到的就是這個(gè);
  2. 由于 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)如下功能:

  1. 用戶登錄后方可進(jìn)入系統(tǒng);
  2. 假定一個(gè) message 的安全內(nèi)容,用戶可以創(chuàng)建 message 的內(nèi)容,但是如果需要修改 / 刪除 message 的內(nèi)容就必須具有相應(yīng)的權(quán)限;
  3. Admin 具有所有的權(quán)限;
  4. 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 能夠給我們帶來什么樣的驚喜?

使用步驟如下:

  1. 安裝 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ù)庫的 Realm
    • grails create-ldap-realm,創(chuàng)建一個(gè)訪問 ldap 的 Realm
    • grails create-wildcard-realm,創(chuàng)建一個(gè)訪問數(shù)據(jù)庫的 Realm,但使用的是 Shiro 的 WildcardPermission。
    • grails quick-start,它是 create-auth-controller 和 create-db-realm 的集合體,是 Shiro 的快速入門,在接下來的內(nèi)容中將詳細(xì)介紹它的使用。
  2. 下面進(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 的訪問控制。
  3. 啟動(dòng)程序,當(dāng)訪問 controller 的時(shí)候,頁面就會(huì)重定向到登錄頁面。但是這個(gè)時(shí)候是無法登錄,因?yàn)槲覀儧]有添加用戶。
  4. 進(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 登陸了。

  5. 在 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。

  6. 授權(quán)部分內(nèi)容參見上一示例,這里不做冗述。
  7. 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.rar2MHTTP

關(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。

建議

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    狠狠做五月深爱婷婷综合| 自拍偷拍一区二区三区| 国产成人亚洲欧美二区综| 国产伦精品一区二区三区精品视频 | 国产精品久久熟女吞精| 亚洲五月婷婷中文字幕| 亚洲a码一区二区三区| 中文字幕日韩欧美一区| 久七久精品视频黄色的| 久草精品视频精品视频精品| 国产精品白丝久久av| 日系韩系还是欧美久久 | 国产不卡最新在线视频| 91人人妻人人爽人人狠狠| 国产毛片对白精品看片| 国产综合一区二区三区av| 日本在线视频播放91| 精品国产av一区二区三区不卡蜜 | 欧美精品日韩精品一区| 国产在线日韩精品欧美| 日本在线高清精品人妻| 好吊妞视频这里有精品| 亚洲欧美日产综合在线网| 欧美激情中文字幕综合八区| 亚洲欧美日韩网友自拍| 欧美偷拍一区二区三区四区| 国产精品成人一区二区三区夜夜夜 | 欧美日韩国产福利在线观看| 国产成人精品久久二区二区| 国产在线日韩精品欧美| 果冻传媒精选麻豆白晶晶 | 亚洲中文在线男人的天堂| 亚洲熟女乱色一区二区三区| 最新午夜福利视频偷拍| 99国产成人免费一区二区| 日本精品视频一二三区| 欧美日韩亚洲综合国产人| 国产精品人妻熟女毛片av久 | 中文文精品字幕一区二区| 国产精品免费福利在线| 日韩高清中文字幕亚洲|