当前位置:  开发笔记 > 编程语言 > 正文

为什么用户可以使用Spring Security和Grails进行更新而无需调用save()?

如何解决《为什么用户可以使用SpringSecurity和Grails进行更新而无需调用save()?》经验,为你挑选了1个好方法。

概述:

User,UserRole和Role几乎是Grails中SpringSecurity插件的标准模型

我确保(在控制器中)的一件事是每个用户只有一个角色

我的引导程序中创建了3个角色:管理员,管理员和用户

管理员可以执行所有操作

管理员可以更新其他管理员和用户

用户只能更新自己(但不能更新他们的角色)

在我的控制器中,我通常使用注释来控制大多数安全性,但是对于更新和保存操作,我添加了用于更高级检查的逻辑:

@Transactional
def update(User userInstance) {
    User currentUser = User.get(springSecurityService.currentUser?.id)
    Role currentRole = currentUser.getAuthorities().getAt(0)    // There is only 1 role per user, so give the first

    Role roleInstance = Role.get(params['role.id'])

    // SECURITY LOGIC -> Move to service
    if (currentRole.authority.equals("ROLE_USER")) {

        if (userInstance != currentUser || !roleInstance.authority.equals("ROLE_USER")) {
            notAllowed(userInstance)
            return
        }
    } else if (currentRole.authority.equals('ROLE_MANAGER')) {
        ...
    }
    ...
    // REST OF CODE - User is saved here
}

现在这是我遇到一个奇怪的问题的地方。如果我以ROLE_USER身份登录并更新ROLE_ADMIN,则会收到我应有的notAllowed错误消息,并且该操作将立即返回,因此不会继续到实际保存用户的REST OF CODE。

如果我查看管理员,则实际上它已被更新(持久化​​)。为什么会这样,因为它从来没有调用过save()?

谢谢!



1> Burt Beckwit..:

这与Spring Security无关-这只是巧合,在处理插件使用的域类时会发生这种情况。

默认情况下,Grails使用“在视图中打开会话”模式,这在使用Hibernate时很常见。在每个请求的开始,都会创建一个Hibernate会话并将其存储在ThreadLocal中,并且持久性代码将在可用时使用该会话,并在请求结束时刷新并关闭该会话。

在处理延迟加载的实例和集合时,这特别有用。如果没有可用的现有Hibernate会话,则持久性代码将创建一个持久性代码,并使用它从数据库中检索实例,但是由于它创建了该会话,因此它将在查询完成后将其关闭。这将使实例与任何会话断开连接,并且没有自动重新连接逻辑,因此,如果在断开对象连接后尝试访问未初始化的延迟加载实例或集合,则会出现异常。但是,如果已经有一个打开的会话,则持久性代码会使用它,但不会关闭它,因此将附加已加载的实例,并且可以进行延迟加载。

您所看到的是Hibernate检测到一个持久实例已被修改,并且默认情况下,当会话关闭时,它将检测到这些更改并有帮助地将它们刷新到数据库中。无论有无save()呼叫,都会发生这种情况,因此实际上,通常唯一需要呼叫的时间save()是插入新实例时。

您可以在视图支持中禁用打开的会话,但是这样做会使您损失很多,通常这不是一个好主意。您还可以自定义其工作方式,何时进行刷新等。但是通常,您应该断开不想自动刷新的附加实例。有一个GORM方法discard()--如果在经过修改的实例上调用它,则在刷新发生时Hibernate不会意识到它,并且将不会保存任何内容。

不相关-此行

User currentUser = User.get(springSecurityService.currentUser?.id)

应该只是

User currentUser = springSecurityService.currentUser

因为该getCurrentUser()方法使用来自安全认证的缓存ID从数据库中检索User实例。您正在使用该User实例获取其ID,然后将其丢弃,并使用该ID重新加载同一User。

推荐阅读
罗文彬2502852027
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有