精通 Grails: 身份验证和授权(2)

来源:developerWorks 中国 作者:Scott Davis
  


基本授权

现在 Blogito 已经实现了身份验证,接下来是限制您所能执行的操作。例如,任何人都应当能够读取 Entry,但是只有登录用户能够创建、更新和删除 Entry。要达到这个目的,Grails 提供了一个 beforeInterceptor,顾名思义,它为您提供一个钩子,可以在调用目标闭包之前对行为进行授权。

将清单 7 中的代码添加到 EntryController:


清单 7. 向 EntryController 添加授权
				
class EntryController {

  def beforeInterceptor = [action:this.&auth, except:["index", "list", "show"]]

  def auth() {
    if(!session.user) {
      redirect(controller:"user", action:"login")
      return false
    }
  }

  def list = {
    //snip...
  }
}

auth 和 list 之间微妙但重要的一点区别是 list 是一个闭包,而 auth 是一个私有方法(闭包在定义中使用等号;方法使用圆括号)。闭包以 URI 的形式被公开给最终用户;方法则无法从浏览器中进行访问。

auth 方法将执行检查,查看某个 User 是否在会话中。如果不在的话,它将重定向到登录屏幕并返回 false,阻塞初始的闭包调用。

在 beforeInterceptor 调用每个闭包之前,auth 方法将得到调用。该操作使用 Groovy 标记来指向 this 类的 auth 方法,该方法使用了 ampersand(&)字符。except 列表包含了应当从 auth 调用中移除的闭包。如果希望拦截一些闭包调用,可以使用 only 替换 except(有关 beforeInterceptor 的更多信息,参见 参考资料)。

重新启动 Grails 并测试 beforeInterceptor。尝试在未登录的情况下访问 http://localhost:9090/blogito/entry/create。您应当被重定向到登录屏幕。以 jsmith 身份登录并重新尝试。这一次您应当能够成功创建新的 Entry。





细粒度授权

beforeInterceptor 提供的粗粒度授权仅仅是个开始,但是也可以向单独的闭包添加授权钩子。例如,任何已登录的 User(不仅仅是初始创建者)都可以编辑任何 Entry。可以关闭安全漏洞:将 4 行良好布置的代码添加到 EntryController.groovy 中的 edit 闭包中,如清单 8 所示:


清单 8. 向 edit 闭包添加授权
				
def edit = {
    def entryInstance = Entry.get( params.id )
    
    //limit editing to the original author
    if( !(session.user.login == entryInstance.author.login) ){
      flash.message = "Sorry, you can only edit your own entries."
      redirect(action:list)
    }

    if(!entryInstance) {
        flash.message = "Entry not found with id ${params.id}"
        redirect(action:list)
    }
    else {
        return [ entryInstance : entryInstance ]
    }
}

您可以(也应该)使用相同的四行代码锁定 delete 和 update 闭包。如果来回复制和粘帖相似代码的工作非常繁琐(并且应当会如此),那么可以创建一个单一的私有方法并在所有三个闭包中调用它。如果发现在许多控制器内使用的是相同的 beforeInterceptor 和私有方法,那么可以将常见的行为解析为单个主控制器,并使用其他控制器扩展它,就像在任何 Java 类中所做的那样。

可以向授权基础设施添加另外一项内容以使它变得更加健壮:角色





添加角色

为 User 分配角色是一种方便的分组方法。随后可以向组分配权限,而不是向个人分配权限。例如,现在任何人都可以创建一个新的 User。仅仅检查某个用户是否登录还远远不够。我希望限制管理员管理 User 帐户的权限。

清单 9 向 User 添加了一个角色字段以及一条限制,限制 author 或 admin 的值:


清单 9. 向 User 添加一个角色字段
				
class User {
  static constraints = {
    login(unique:true)
    password(password:true)
    name()
    role(inList:["author", "admin"])
  }
  
  static hasMany = [entries:Entry]
  
  String login
  String password
  String name
  String role = "author"
  
  String toString(){
    name
  }
}

注意,role 默认值为 author。inList 限制给出了一个复选框,只显示了两个有效选项。图 4 展示了它的实际使用:


图 4. 将新用户角色限制为 author 或 admin

在 grails-app/conf/BootStrap.groovy 中创建一个 admin User,如清单 10 所示。不要忘记将 author role 添加到两个现有的 User 中。


清单 10. 添加一个 admin User
				
import grails.util.GrailsUtil

class BootStrap {
  def init = { servletContext ->
    switch(GrailsUtil.environment){
      case "development":
        def admin = new User(login:"admin", 
                             password:"password", 
                             name:"Administrator", 
                             role:"admin")
        admin.save()
      
        def jdoe = new User(login:"jdoe", 
                            password:"password", 
                            name:"John Doe", 
                            role:"author")
        //snip...
        
        def jsmith = new User(login:"jsmith", 
                            password:"wordpass", 
                            name:"Jane Smith", 
                            role:"author")
        //snip...
        
      break

      case "production":
      break
    }

  }
  def destroy = {
  }
}

最后,添加清单 11 中的代码,将所有 User 帐户活动限制为只有拥有 admin 角色的人员才能执行:


清单 11. 将 User 帐户管理限制为只有拥有 admin 角色的人员才能执行
				
class UserController {
  
  def beforeInterceptor = [action:this.&auth, 
                           except:["login", "authenticate", "logout"]]

  def auth() {
    if( !(session?.user?.role == "admin") ){
      flash.message = "You must be an administrator to perform that task."
      redirect(action:"login")
      return false
    }
  }
  
  //snip...
}  

要测试基于角色的授权,以 jsmith 身份登录并随后尝试访问 http://localhost:9090/blogito/user/create。应当被重定向到登录屏幕,如图 5 所示:


图 5. 阻塞非管理员访问

现在以 admin 用户的身份登录。应当能够访问所有的闭包。





使用插件实现更高级功能

这个 “微型” 博客应用程序的 “微型” 身份验证和授权系统现在已经初具雏形。您可以轻松地对它进行扩展。也许您希望 User 能够管理他们各自的帐户,而不是其他人的。也许 admin 应当具备编辑所有 Entries 的能力,而不仅仅是编辑他们自己的。在这些情况下,只需要策略性地放置几行代码就可以添加新的功能。

人们常常将简洁性误解为缺乏功能。Blogito 仍然不足 200 行代码 — 并且这还包含了单元和集成测试。在命令行输入 grails stats 以确认这点。结果如清单 12 所示。但是 Blogito 不复杂并不表示它的功能不完备。


清单 12. “微型” 应用程序的大小
				
$ grails stats

	+----------------------+-------+-------+
	| Name                 | Files |  LOC  |
	+----------------------+-------+-------+
	| Controllers          |     2 |    95 | 
	| Domain Classes       |     2 |    32 | 
	| Tag Libraries        |     2 |    21 | 
	| Unit Tests           |     5 |    20 | 
	| Integration Tests    |     1 |    10 | 
	+----------------------+-------+-------+
	| Totals               |    12 |   178 | 
	+----------------------+-------+-------+

时间:2009-06-19 11:05 来源:developerWorks 中国 作者:Scott Davis 原文链接

好文,顶一下
(0)
0%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量