grails 入门指南_使用Spring Security在Grails中进行方法安全性入门

grails 入门指南

这篇博客文章将介绍使用Spring Security插件在Grails中使用安全性表达式实现方法级安全性。 我假设您对Grails Spring Security Core插件有一些基本的了解。

角色还不够。

使用Spring Security Core插件时,通常会开始配置访问某些URL所需的角色。 可以通过使用配置映射(请参见下文),使用@Secured注释对控制器操作进行注释或通过将RequestMap存储在数据库中(请参见: 存储在数据库中的Requestmap实例 )来完成此配置。

角色配置示例:

grails.plugins.springsecurity.interceptUrlMap = [
  // /admin/** URLs can only be accessed by users with role ROLE_ADMIN
  '/admin/**' : ['ROLE_ADMIN'], 
  ...
]

角色可以很好地定义简单的规则,例如仅允许管理员访问管理员功能 。 不幸的是,这常常是不够的。

考虑一个用户可以在其中创建某种内容(例如评论,新闻等)的应用程序。 用户应该能够编辑他以后创建的内容。 但是,用户不应能够编辑其他用户创建的内容。 在这里,可以使用一个角色来检查用户是否对编辑内容功能具有一般访问权限。 遗憾的是,角色对于检查是否允许特定用户编辑特定内容没有太大帮助。 这是安全性表达式和方法安全性跃入的地方。

@Secured注释

如果您使用过Spring Security(没有Grails),则可能还记得Spring Security的@Secured批注。 Grails Spring Security Core插件还包含此注释的Grails替代品。 grails版本(@ grails.plugins.springsecurity.Secured)也可用于字段,而原始版本(@ org.springframework.security.access.annotation.Secured)仅可用于方法。 这允许Grails批注与包含闭包值的字段一起使用:

@Secured(['ROLE_ADMIN']) // only works with @grails.plugins.springsecurity.Secured
def adminAction = {
  ...
}

另外,Grails @Secured注释还支持SpEL表达式,而标准的Spring Security @Secured注释仅支持角色检查(有关更多详细信息,请参阅文档 )。

服务如何?

安全约束通常是您在服务层中想要的。 不幸的是,Grails Spring Security Core插件使@Secured注释仅在控制器中可用。 要使用服务级别的安全注释,我们必须添加Grails Spring Security ACL插件。 ACL插件还提供了更复杂的@PreAuthorize和@PostAuthorize批注。 这些注释可用于在方法执行之前和之后验证对方法的访问(我们将在以后看到)。 Grails Spring Security ACL插件的主要目的是提供对访问控制列表(ACL)的支持,该列表允许对访问权限进行非常精细的控制。 也许将来我会写一篇有关ACL用法的博客文章,但是绝对超出了本文的范围。 在这里,我们仅使用ACL插件在批注中获得安全性表达式支持。 不需要进一步的插件配置。

那代码呢?

好的,假设我们要构建一个用户可以管理笔记的应用程序。 域类如下所示:

class Note {
  String title
  String text
  static belongsTo = [author: User]
}
class User {
  String username
  String password
  static hasMany = [notes: Note]

  static constraints = {
    username blank: false, unique: true
    ...
  }

  // .. rest of generated user class from Spring Security Core plugin 
}

用户可以有许多注释,而注释只有一个作者。 现在,我们要创建一个服务,该服务提供了一些使用Note对象的常用方法:

class NoteService {
  public long getTotalNoteCount() { .. }
  public void createNote(Note note) { .. }
  public void updateNote(Note note) { .. }
  public Note getNote(long id) { .. } 
  public void removeNote(long id) { .. }
}

对于这些服务方法,我们要应用以下访问规则:

  • 每个人都应该能够使用getTotalNoteCount()获取系统存储的笔记总数
  • 登录的用户可以使用createNote()创建新的笔记
  • 作者只能阅读,更新或删除注释

我们首先将@PreAuthorize添加到getTotalNoteCount():

@PreAuthorize('permitAll()')
public long getTotalNoteCount() {
  ...
}

@PreAuthorize和@PostAuthorize将SpEL表达式作为参数,对其进行评估以确定是否授予用户访问权限。 每个人都应该能够调用getTotalNoteCount(),因此我们只需调用预定义的allowAll()函数。

createNote()的安全性表达式类似:

@PreAuthorize('isFullyAuthenticated()')
public Note createNote(Note note) {
  ...
}

由于只有登录的用户才可以创建注释,因此我们在表达式内调用isFullyAuthenticated()函数。

到目前为止,使用角色可以达到相同的效果。 在下一个示例中,我们将看到安全性表达式的真正好处。 updateNote()的访问规则稍微复杂一些:

@PreAuthorize('isAuthenticated() and principal?.username == #note.author.username')
public Note updateNote(Note note) {
  ...
}

更新便笺时,我们必须确保登录用户是他要编辑的便笺对象的作者。 Spring Security使用名为主体的预定义变量填充SpEL上下文,该预定义变量可用于访问当前登录的用户(所有预定义变量的列表均可在此处找到)。 使用#前缀可以访问方法参数。 因此,SpEL表达式中的#note引用了note方法参数。 在此示例中,我们检查登录用户的名称(主要)和传递的Note对象的作者(#note.author)的名称是否匹配。 如果两者相同,则允许用户更新Note对象。

getNote()方法有点不同,因为没有可用于访问注释作者的Note对象参数。 但是,返回值是一个Note对象,可以使用@PostAuthorize对其进行检查:

@PostAuthorize('isAuthenticated() and principal.username == returnObject.author.username') 
public Note getNote(long id) {
  ...
}

如上所述,@ PostAuthorize批注可用于在调用方法后评估安全性表达式。 在@PostAuthorize表达式中,可以使用预定义的变量returnObject访问方法调用返回的对象。 如果登录的用户不是返回的Note对象的作者,则将抛出AccessDeniedException。

removeNote()有点复杂。 没有注释对象参数,因此在@PreAuthorize中没有简单的方法来验证注释作者。 @PostAuthorize在这里也无济于事。 即使removeNote()会返回已删除的Note对象,@ PostAuthorize也会检查是否允许用户删除已删除的Note对象。 没那么有用…

在下面的内容中,我将展示将安全约束添加到removeNote()的两种不同方式。

1.使用bean引用

在SpEL表达式中,可以引用bean并将安全规则的评估委派给它们。 看起来像下面的代码:

@PreAuthorize("@securityService.canRemoveNote(#id)")
public Note removeNote(long id) {
  ...
}

@符号用于在SpEL表达式中引用bean。 在这里,调用了名为securityService的bean的canRemoveNote()方法。 便笺ID作为参数传递给canRemoveNote()。 securityService bean是标准的Grails服务,用于实现安全约束:

class SecurityService {

  def springSecurityService

  public boolean canRemoveNote(long id) {
    Note note = Note.get(id)
    return note.author == springSecurityService.getCurrentUser()
  }
}

不幸的是,这不会立即可用,并且需要对Spring Security配置进行一些小的调整。 不久前,我写了一篇关于此的简短文章,因此在此不再赘述。 请查看此博客文章以获取更多详细信息: 在Spring Security表达式中调用bean方法

2.使用PermissionEvaluator

实现removeNote()的访问规则的另一种解决方案是使用Spring Security的内置hasPermission()方法将安全性检查委托给PermissionEvaluator。 在安全性表达式上下文中,可以使用两种不同的hasPermission()方法:

hasPermission(Object targetObject, Object permission)
hasPermission(Serializable targetId, String targetType, Object permission)

如果必须检查的对象实例可用(例如在updateNote(Note note)中),则可以使用第一个。 可以使用第二个版本,没有可用的实例,并且需要通过ID和类型来标识对象。 后面的一个可以帮助我们使用removeNote()方法:

@PreAuthorize("hasPermission(#id, 'com.mscharhag.Note', 'remove')")
public Note removeNote(long id) {
  ..
}

请注意,id的原始类型long被转换为Long,从而实现了所需的Serializable接口。 现在,我们必须创建自己的PermissionEvaluator实现来定义我们的安全约束。 PermissionEvaluator要求实现两个方法,这些方法与可在安全性表达式中使用的两个hasPermission()方法直接相关:

boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) 
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)

唯一的区别是Spring Security将当前身份验证状态作为附加参数传递给PermissionEvaluator。 PermissionEvaluator的可能实现如下所示:

class GrailsPermissionEvaluator implements PermissionEvaluator {

  def grailsApplication
  def springSecurityService 

  @Override
  public boolean hasPermission(Authentication authentication, Object note, Object permission) {
    def user = springSecurityService.getCurrentUser();
    return permission == 'remove' && note.author == user
  } 

  @Override
  public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
    // get domain class with name targetType
    Class domainClass = grailsApplication.getDomainClass(targetType).clazz

    // get domain object with id targetId
    Note note = domainClass.get(targetId)

    return hasPermission(authentication, note, permission)
  }
}

如我们所见,第二种方法将targetType和targetId解析为Note对象,然后将其传递给第一种方法。 第一种方法检查是否允许当前登录的用户删除Note对象。

为了完成这项工作,我们需要重写由Spring Security ACL插件配置的默认的PermissionEvaluator bean。 这是在grails-app / conf / spring / resources.groovy中完成的:

permissionEvaluator(GrailsPermissionEvaluator) {
  grailsApplication     = ref('grailsApplication')
  springSecurityService = ref('springSecurityService')
}

默认情况下,Grails Spring Security ACL插件将AclPermissionEvaluator实例配置为PermissionEvaluator,该实例可用于评估ACL规则。 但是,在此示例中,我们未使用ACL,因此可以使用我们自己的实现覆盖它。

总而言之,bean引用和PermissionEvaluator方法都是实现自己的安全性约束的一种好方法。 如果您的访问规则比本示例中的规则更复杂,这将特别有用。

最终的NoteService如下所示:

class NoteService { 

  @PreAuthorize('permitAll()')
  public long getTotalNoteCount() { .. }

  @PreAuthorize('isFullyAuthenticated()')
  public Note createNote(Note note) { .. }

  @PostAuthorize('isAuthenticated() and principal.username == returnObject.author.username') 
  public Note getNote(long id) { .. }

  @PreAuthorize('isAuthenticated() and principal.username == #note.author.username')
  public Note updateNote(Note note) { .. }

  @PreAuthorize("@securityService.canRemoveNote(#id)")
  public Note removeNoteUsingBeanResolver(long id) { .. }

  @PreAuthorize("hasPermission(#id, 'com.mscharhag.Note', 'remove')")
  public Note removeNoteUsingPermissionEvaluator(long id) { .. } 
}


翻译自: https://www.javacodegeeks.com/2013/11/getting-started-with-method-security-in-grails-using-spring-security.html

grails 入门指南

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值