使用用户组和角色时Grails / Spring Security中存在错误-无法进行身份验证

bwleehnv  于 2022-11-29  发布在  Spring
关注(0)|答案(3)|浏览(123)

我想我在Grails Spring Security 3.1.1和最新的Grails 3.2.6中发现了一个bug。
我已经安装了Spring Security插件。
在命令行控制台中,我执行了以下操作:

grails s2-quickstart org.softwood.security User Role --groupClassName=UserGroup

创建一个用户、角色和UserGroup表,因为我想使用allocate roles to groups特性。然后,我配置了域类,并在引导程序中添加了一些用户来测试它,如下所示:

def loadSecurityUserAndRoles () {
    //plugin requires ROLE_ prefix see section 4.2/p18

    Role adminRole = new Role(authority: 'ROLE_ADMIN').save(failOnError:true)
    Role userRole = new Role(authority: 'ROLE_USER').save(failOnError:true)
    Role xtraRole = new Role(authority: 'ROLE_XTRA').save(failOnError:true)
    UserGroup adminGroup = new UserGroup (name:"GROUP_ADMIN").save(failOnError:true)
    UserGroup userGroup = new UserGroup (name:"GROUP_USERS").save(failOnError:true)

    User userWill = new User(username: 'will', password: 'password').save(failOnError:true)
    User userMaz = new User(username: 'maz', password: 'password').save(failOnError:true)
    User userMeg = new User(username: 'meg', password: 'password').save(failOnError:true)

    //give adminGroup admin and user roles
    UserGroupToRole sgr = UserGroupToRole.create(adminGroup, adminRole)
    sgr = UserGroupToRole.create(adminGroup, userRole)

    sgr = UserGroupToRole.create(userGroup, userRole)

    assert UserGroupToRole.count() == 3

    def auth2 = adminGroup.getAuthorities()
    println "adminGroup authorities returned $auth2 "

    //assign test user to adminGroup, and maz+meg to user group, inherit all group roles
    UserToUserGroup su2g = UserToUserGroup.create (userWill, adminGroup, true)
    su2g = UserToUserGroup.create (userMaz, userGroup, true)
    su2g = UserToUserGroup.create (userMeg, userGroup, true)

    //assign individual 'xtra' role to user
    UserToRole sxtra = UserToRole.create(userWill, xtraRole, true)
    assert UserToRole.count() == 1

    def auth = userWill.getAuthorities()
    assert auth.collect{it.authority}.sort() == ['ROLE_ADMIN', 'ROLE_USER', 'ROLE_XTRA']
    println "userWill authorities returned $auth "

    def mazAuth = userMaz.getAuthorities()
    def megAuth = userMeg.getAuthorities()
    println "user authorities returned maz: '$mazAuth', and meg: '$megAuth' "

    def groups = userWill.getUserGroups()
    assert groups.collect{it.name}.sort() == ['GROUP_ADMIN']

    assert UserGroup.count() == 2
    assert User.count() == 3
    assert Role.count() == 3
    assert UserToUserGroup.count() == 3
    assert UserGroupToRole.count() == 3
    assert UserToRole.count() == 1

}

这一切看起来都像id预期的那样工作,当我Assert<userInst>.getAuthorities()
然后我设置了一个控制器secureTest,它具有开放操作和安全操作

class SecureTestController {

    def index() {
        render "hello Will you passed the permit_any"
    }

    @Secured ('ROLE_ADMIN')
    def secure () {
        render "hello Will you passed the ROLE_ADMIN"

    }
}

我运行应用程序-它启动了,我将浏览器指向secureTest/index-作为打开的url工作得很好。
当我把浏览器指向secureTest/secure时,它抛出默认登录页面,我在它抛出stacktrace时填写will/password,登录失败。
我认为这条线索的关键部分就在这里:

Caused by: groovy.lang.MissingPropertyException: No such property: authorities for class: org.softwood.security.Role
Possible solutions: authority
    at org.grails.datastore.gorm.GormInstanceApi.propertyMissing(GormInstanceApi.groovy:55)
    at org.grails.datastore.gorm.GormEntity$Trait$Helper.propertyMissing(GormEntity.groovy:57)
    at org.grails.datastore.gorm.GormEntity$Trait$Helper$propertyMissing$9.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
    at org.softwood.security.Role.propertyMissing(Role.groovy)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaClassImpl.invokeMissingProperty(MetaClassImpl.java:880)
    at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1861)
    at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:3735)
    at org.softwood.security.Role.getProperty(Role.groovy)
    at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:172)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:456)
    at grails.plugin.springsecurity.userdetails.GormUserDetailsService$_loadAuthorities_closure2.doCall(GormUserDetailsService.groovy:92)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
    at groovy.lang.Closure.call(Closure.java:414)
    at groovy.lang.Closure.call(Closure.java:430)

我认为这个方法在这里真的失败了(GormUserDetailsService.groovy:92)。
当你点击这个链接时,编辑器会把你带到插件中。

protected Collection<GrantedAuthority> loadAuthorities(user, String username, boolean loadRoles) {
    if (!loadRoles) {
        return []
    }

    def conf = SpringSecurityUtils.securityConfig

    String authoritiesPropertyName = conf.userLookup.authoritiesPropertyName
    String authorityPropertyName = conf.authority.nameField

    boolean useGroups = conf.useRoleGroups
    String authorityGroupPropertyName = conf.authority.groupAuthorityNameField

    Collection<?> userAuthorities = user."$authoritiesPropertyName"
    def authorities

    if (useGroups) {
        if (authorityGroupPropertyName) {
            authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
        }
        else {
            log.warn 'Attempted to use group authorities, but the authority name field for the group class has not been defined.'
        }
    }
    else {
        authorities = userAuthorities.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
    }
    authorities ?: [NO_ROLE]
}

这里的关键部分是以下调用序列:

if (useGroups) {
        if (authorityGroupPropertyName) {
            authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
        }

useGroups为true。我有一个authorityGroupPropertyName,它是通过快速安装脚本在application.groovy文件中设置:

grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities'

因此上面的代码行调用:

userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique()

这将返回一个role.authority名称的hashSet作为字符串,flat/unique只是确保没有嵌套结构,字符串是唯一的。
最后一点我认为是bug。

<hashSet of role Names>.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }

在此位中,对字符串集调用collect方法,但传递给"SimpleGrantedAuthority"的字符串应该只是字符串。而不是其调用

it."$authorityPropertyName"

其中它是一个字符串并且没有这样的属性。
在application.groovy中设置的关键位包括:

grails.plugin.springsecurity.userLookup.userDomainClassName = 'org.softwood.security.User'
grails.plugin.springsecurity.userLookup.authoritiesPropertyName = 'authorities'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'org.softwood.security.UserToUserGroup'
grails.plugin.springsecurity.authority.className = 'org.softwood.security.Role'
grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities' //'authority'
grails.plugin.springsecurity.useRoleGroups = true

正如你所看到的,我试图将权限更改为"authority",因为这是角色类中的属性名称。
我认为这是一个bug,代码应该只传递"it":

.collect {new SimpleGrantedAuthority(it)}

以生成<SimpleGrantedAuthority>类型的hashSet。
有没有其他人遇到过Spring Security的这个问题?我不敢相信我是第一个遇到这个问题的人,或者也许没有人试图使用组?

0s7z1bwu

0s7z1bwu1#

我也配置了用户角色和角色组,这是我的工作方式:
在用户类中:

Set<RoleGroup> getAuthorities() {
        UserRoleGroup.findAllByUser(this)*.roleGroup
    }
   //not used
   // Set<RoleGroup> getAuthoritiesNames() {
   //     UserRoleGroup.findAllByUser(this)*.roleGroup?.name
   // }

    Set<Role> getAuthority() {
        UserRole.findAllByUser(this).collect { it.role } as Set
    }

在RoleGroup.groovy中,我有:(想我改了这些不确定的话)

Set<Role> getAuthorities() {
        RoleGroupRole.findAllByRoleGroup(this)*.role
    }
    def getAuthority() {
        RoleGroupRole.withTransaction {
            RoleGroupRole.findAllByRoleGroup(this)*.role.authority[0]
        }
    }

在我的引导程序中,类似下面的代码创建了绑定到角色和角色组的默认管理员帐户:

private void addAdminUser(String username) {
        User adminUser = User.findByUsername(username)
        if (!adminUser) {

            User.withTransaction {
                adminUser = new User(username: username, password: 'PASSWORD'
                )
                adminUser.save(flush: true)
            }
        }
        def adminRole
        Role.withTransaction {
            adminRole= Role.findByAuthority('ROLE_ADMIN')
            if (!adminRole) {
                adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true)
                UserRole.create adminUser, adminRole,true
            }
        }
        def adminRoleGroup
        RoleGroup.withTransaction {
            adminRoleGroup = RoleGroup.findByName('ADMINS')
            if (!adminRoleGroup) {
                adminRoleGroup = new RoleGroup(name: 'ADMINS').save(flush: true)
            }
        }
        UserRoleGroup.withTransaction {
            def adminRoleGroupRole = RoleGroupRole.findByRole(adminRole)
            if (!adminRoleGroupRole) {
                adminRoleGroupRole = new RoleGroupRole(role: adminRole, roleGroup: adminRoleGroup).save(flush: true)
                new UserRoleGroup(user: adminUser, roleGroup: adminRoleGroup).save(flush: true)
            }
        }
    }
mccptt67

mccptt672#

我想我已经剥开洋葱皮,找到了问题所在。
第1步:我从头开始创建了一个新项目,并在空项目上添加了grails-security插件。
这个方法奏效了,所以我把它产生的结果和我自己进入的情况进行了比较,我想我明白了我的问题在哪里。
我的角色类和空测试一一样--没有区别。
然后我进入了User类,不知何故,我得到了返回Set<Role>的getAuthorities()(这似乎是合理的,因为我的UserGroup(在新的开始中也称为RoleGroup)中的getAuthorites()返回Set<Role>,而基本的User/Role返回Set<Role>
如果生成的项目只有User和Role,那么User.getAuthorities()将返回set<Role>。但是,当使用组时,模板将发生更改,并返回Set<UserGroup>
这种差异是至关重要的,因为调整其取消引用策略的代码是在GormUserDetailsService.loadAuthorities()的代码中,在那里看不到它--参见我一直试图修复的"调整版本"。

if (useGroups) {
        if (authorityGroupPropertyName) {
            //authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
            //ww edit to stop gpf..  userAuthorities is Set<Role>, so first collect gets the names and produces ArrayList of string
            //second collect builds the authorities as Set<SimpleGrantedAuthority>
            authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it) }
        }
        else {
            log.warn 'Attempted to use group authorities, but the authority name field for the group class has not been defined.'
        }
    }
    else {
        authorities = userAuthorities.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
    }

这样做的问题是def authorities变量会根据使用的是用户/角色模型还是用户/组/角色模型而改变类型。
我摔在中间;我尝试使用组(默认分配模型),但允许单独的角色分配(单独授予的角色,作为覆盖工具)。
当我读取User.authorities属性时-我假设这将返回它所链接的角色(通过组,并通过我的个人分配覆盖)(就像在单用户/角色模型中一样)。

Set<Role> getAuthorities() {
    //orig UserUserGroupBroken.findAllByUser(this)*.userGroup

    Set<Role> individualRoles = UserToRole.findAllByUser(this)*.role
    Set<UserGroup> groups = UserToUserGroup.findAllByUser(this)*.group
    Set<Role> groupRoles = groups.collect{it.getAuthorities() }
    Set<Role> aggregateRoles = new HashSet()
    aggregateRoles.addAll (groupRoles.flatten())
    aggregateRoles.addAll (individualRoles.flatten())
    aggregateRoles
}

因此,这一切看起来都是合理的,并且做了我想要的(我的测试显示通过叠加分配了正确的角色)。然而,GormUserDetailsService没有预料到这一点,并且中断了,就好像useGroups为真一样,它取消引用两个集合以获得Set<Role>,并使用它来构建SimpleGrantedAuthority集合。
这样的结果就是我的目的被打败了:这与假设的模型相差太远了。
我会在Git网站上把它作为一个建议,因为它会更干净,更不透明,我想你直觉上会认为用户的authorities属性会给你一个Set<Role>
现在,我们必须坚持基本的用户/组/角色模型,并接受它。

4szc88ey

4szc88ey3#

我将代码从GormUserDetailsService复制到我的引导程序中,这样我就可以在自己的文件空间中扩展expand/play。
我修改了if块,并像这样展开:

if (useGroups) {
            if (authorityGroupPropertyName) {
                //userAuthorities returns Set<Role>
                println """ debug
authoritiesPropertyName = $authoritiesPropertyName
authorityPropertyName = $authorityPropertyName
authorityGroupPropertyName = $authorityGroupPropertyName
userAuthorities returns $userAuthorities of type ${userAuthorities.getClass()}

"""
                 def roles = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique()

                authorities = roles.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }

我的调试字符串在控制台上显示了这一点

debug
authoritiesPropertyName = authorities
authorityPropertyName = authority
authorityGroupPropertyName = authority
userAuthorities returns [Role(authority:ROLE_XTRA), Role(authority:ROLE_USER), Role(authority:ROLE_ADMIN)] of type class java.util.HashSet

返回到变量'roles'的第一个结果是String的ArrayList(每个角色.权限名称示例.具有此用户的正确值,如在userWill的早期引导程序设置中一样)。
接下来的代码失败了,因为我有一个字符串的ArrayList,它试图访问一个属性:

authorities = roles.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }

失败原因:

groovy.lang.MissingPropertyException: No such property: authority for class: java.lang.String
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:458)
    at coffeeshopapp.BootStrap$_loadSecurityUserAndRoles_closure6.doCall(BootStrap.groovy:115)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
    at groovy.lang.Closure.call(Closure.java:414)
    at groovy.lang.Closure.call(Closure.java:430)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.collect(DefaultGroovyMethods.java:3170)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.collect(DefaultGroovyMethods.java:3140)
    at org.codehaus.groovy.runtime.dgm$66.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at coffeeshopapp.BootStrap.loadSecurityUserAndRoles(BootStrap.groovy:115)

不管我用哪种方式剪切它,原始源代码中的这一行都不起作用。原始行再次显示:

authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }

如果它读起来像这样-代码将工作:

authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it) }

肯定的代码在v3.1.1插件不能工作?

相关问题