Grails 中的 Spring Security 提供授权

Posted

技术标签:

【中文标题】Grails 中的 Spring Security 提供授权【英文标题】:Spring Security in Grails providing authorization 【发布时间】:2013-08-24 08:34:58 【问题描述】:

这里的建议将不胜感激。

我们使用 Spring Security 域对象安全性来锁定对 Grails 应用程序中特定对象的访问。我们也有扩展权限。我们使用普通的@PreAuthorize 注解来控制对域对象的访问,具体取决于分配给特定用户的权限。

在此架构中,我希望一位用户能够邀请另一位用户加入群组。为此,第二个用户会收到来自第一个用户的邀请以加入具有特定权限的组。第二个用户可以选择接受或拒绝邀请。

鉴于域对象属于第一个用户,而不是第二个用户,我如何授予第二个用户对具有所提供权限的域对象的访问权限?

我尝试在服务方法上使用 @PreAuthorize("permitAll") 破解解决方案,以查看是否可行。虽然不要求安全作为授予对象设置域对象特权的特权,但它至少会让我继续前进。但不高兴,我继续收到“accessDenied for class”错误,大概是因为当前用户是第二个用户,而不是域对象所有者。

我是否需要完成 SpEL 建议 here?不幸的是,我几乎不明白 bean 解析器是如何工作的。

编辑:即使我在调用时验证邀请有效,当尝试插入新用户的第一个 ACE 时,Spring ACL 也会抛出异常。异常发生在insertAce调用的void setPermissions(ObjectIdentity oid, recipient, Collection permissions)中:

void setPermissions(ObjectIdentity oid,收件人,集合权限) Sid sid = createSid(recipient)

MutableAcl acl
try 
    acl = aclService.readAclById(oid)

catch (NotFoundException e) 
    acl = aclService.createAcl(oid)


int deleted = 0
acl.entries.eachWithIndex  AccessControlEntry ace, int i ->
    if (ace.sid == sid) 
        acl.deleteAce(i-deleted)
        deleted++
    


permissions.each 
    acl.insertAce(acl.entries.size(), it, sid, true)


aclService.updateAcl acl

我假设访问被拒绝是因为当前用户(发出邀请时不存在)无权设置其他用户拥有的对象的权限。

【问题讨论】:

所以@PreAuthorize 并没有真正授权到ACL。要访问受 ACL 保护的域对象,需要当前用户具有访问权限。所以要么我完全破解 ACE 插入,要么我想办法在这种特殊情况下授予权限。 那么我可以使用 RunAs 身份验证替换吗?看起来它会完成这项工作。 static.springsource.org/spring-security/site/docs/3.0.x/… 嗯,它应该工作,但它没有。我在数据库中创建了一个新角色 RUN_AS_INVITED_USER,使用 grails.plugins.springsecurity.useRunAs = true 启用了 RUNAss,设置 grails.plugins.springsecurity.acl.authority.changeAclDetails = 'ROLE_RUN_AS_INVITED_USER' 然后用 @Secured(['ROLE_USER ', 'RUN_AS_INVITED_USER']) 但仍然没有喜悦。更糟糕的是,当我使用 springSecurityService.getPrincipal().getAuthorities() 查询方法中的当前角色时,我看不到我的新角色集。 【参考方案1】:

您不仅可以将Role 类用于通用角色(AdminUser 等),还可以用于特定于应用程序的角色。只需允许用户为资源创建Role,然后允许他们的受邀者被授予该角色。 Spring Security 带有一个方便的ifAnyGranted() 方法,它接受一个逗号分隔的角色名称字符串。在资源入口点只需确保授予特定角色:

class Conversation
     Role role

class ConversationController
    def enterConversation()
       // obtain conversation instance 
       if(!SpringSecurityUtils.ifAnyGranted(conversationInstance.role.authority)response.sendError(401)
    

【讨论】:

谢谢,但我不确定这是否能满足我的需要。虽然我可以检查以确保资源入口点的对象(邀请,因为用户可能尚未在服务上注册),但我发现在设置 ACL 权限时我仍然遇到异常。 由于接受邀请是将来可能发生也可能不会发生的事件,因此需要在身份验证周期之外正确处理一个完全独立的用例场景。您可以查看电子邮件确认插件以获取“确认”部分的示例实现,其中包括调度域类、处理超时的石英作业,并使用事件侦听器 API 对各种场景结果做出反应。无论哪种情况,在授予他们角色之前,您肯定需要一个注册用户。希望有所帮助。 问题是我没有授予新用户角色,而是授予新用户访问域对象的权限。【参考方案2】:

结果证明是使用 RunAs。对于 Grails,我这样做如下:

    创建一个特定的角色,允许受邀用户充当管理员以访问受保护的对象。在 Bootstrap 中,确保将此角色加载到 SecRole 域中:

    def邀请角色 = SecRole.findByAuthority('ROLE_RUN_AS_INVITED_USER') 如果(!invitedRole) 受邀角色 = 新的 SecRole() 受邀角色.authority = "ROLE_RUN_AS_INVITED_USER" 受邀角色.save(failOnError:true,flush:true)

    确保在 config.groovy 中角色可以更改目标 object:grails.plugins.springsecurity.acl.authority.changeAclDetails = 'ROLE_RUN_AS_INVITED_USER'

    在 config.groovy 中启用 RunAs

    grails.plugins.springsecurity.useRunAs = true grails.plugins.springsecurity.runAs.key = [key]

    注释服务方法

@PreAuthorize([检查这确实是受邀用户]) @Secured(['ROLE_USER', 'RUN_AS_INVITED_USER'])

一切正常。诀窍是让 ROLE_RUN_AS_INVITED_USER 能够更改 acl。

参考: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/runas.html http://grails-plugins.github.io/grails-spring-security-acl/docs/manual/guide/2.%20Usage.html#2.5%20Run-As%20Authentication%20Replacement

【讨论】:

以上是关于Grails 中的 Spring Security 提供授权的主要内容,如果未能解决你的问题,请参考以下文章

如何使 Grails Spring Security 2.0 中的登录/页面成为初始屏幕?

Grails:乐观锁定,StaleObjectStateException 与 Spring Security 会话中的域,更新计数器

如何从 Grails 中的 Spring Security 获取普通密码或解密密码?

如何处理 grails spring-security-rest 插件中的自定义身份验证异常?

Grails 3.0 中的 Spring Boot Security login.html 位置

身份验证失败 - grails spring security中的错误凭据