使用 Spring Security Grails 插件编码密码

Posted

技术标签:

【中文标题】使用 Spring Security Grails 插件编码密码【英文标题】:Encoding a password with the Spring Security Grails plugin 【发布时间】:2016-01-01 13:31:12 【问题描述】:

在我的 Grails 2.5.1 项目中,我使用 spring-security-core:2.0-RC5 插件并收到此错误:

Error initializing the application: Cannot invoke method encodePassword() on null object

这是我的引导程序:

class BootStrap 

    def init =  servletContext ->
        def springSecurityService
        def userRole = SecurityRole.findByAuthority("ROLE_USER") ?: new SecurityRole(authority:"ROLE_USER").save(flush:true)
        def adminRole = SecurityRole.findByAuthority("ROLE_ADMIN") ?: new SecurityRole(authority:"ROLE_ADMIN").save(flush:true)

        def user = new User(username:"user" ,password:springSecurityService.encodePassword("123"),enable:true ).save(flush:true)
        def admin = new User(username:"admin" ,password:springSecurityService.encodePassword("1234"),enable:true ).save(flush:true)
        SecurityUserSecurityRole.create(user, userRole)
        SecurityUserSecurityRole.create(admin, adminRole)
    
    def destroy = 
    

我缺少什么吗?

【问题讨论】:

【参考方案1】:

问题是def springSecurityService 的位置。正如您声明的那样,它是 init 闭包的局部变量。它不是依赖注入的候选对象,所以没有初始化它,它是空的。

要使用依赖注入,请将 bean 声明为类范围属性,而不是局部变量(闭包或方法)。这样做的原因是 Groovy 编译器将属性声明转换为带有 getter 和 setter 的私有字段。 Spring 不支持 Groovy 属性,但是当有一个与 bean 名称一致的 setter 方法时,它可以按名称自动装配(这是 Grails 的默认设置)。因此,由于 Groovy 编译器为您添加了一个 void setSpringSecurityService(springSecurityService) 方法,Spring 会看到它并调用它,因为该方法的“属性”名称与您想要的 bean 匹配。如果你声明一个局部变量,它会被完全忽略。

所以你的代码应该是这样的:

def springSecurityService

def init =  servletContext ->
    ...

但是您的代码会遇到第二个问题。假设您没有更改 User 类中的自动哈希逻辑,您将编码两次。一次在 BootStrap 中,然后在域类中。如果您这样做,没有人将能够进行身份验证。选择一个 - 像您正在做的那样明确地散列并从域类中删除代码,或者留下域类代码并设置明文密码。如果您选择选项 #2,您的代码(也被清理以使用 findOrSaveBy 并用一个替换所有那些不必要的急切刷新调用)应该看起来像

class BootStrap 

    def init = 
        def userRole = SecurityRole.findOrSaveByAuthority('ROLE_USER')
        def adminRole = SecurityRole.findOrSaveByAuthority('ROLE_ADMIN')

        def user = new User(username: 'user' ,password: '123').save()
        def admin = new User(username: 'admin' ,password: '1234').save()
        SecurityUserSecurityRole.create(user, userRole)
        SecurityUserSecurityRole.create(admin, adminRole)

        User.withSession  it.flush() 
    

更好的方法是将此代码移至事务服务。这留给读者作为练习。

【讨论】:

感谢您的详细回答,您现在已经对我说清楚了,但我面临着一个新事物,除了 userRole 之外没有任何内容存储在数据库中adminRole,不存储用户!!你在代码中看到什么了吗? 这可能是一个验证错误 - 您是否向 User 类添加了新的必需属性?所有属性默认为nullable:false。我几乎从不使用failOnError,因为抛出验证异常的成本很高,但是当你有硬编码的值时,你希望像这里一样有效(另一种情况是测试中的数据),我会用它来制作问题很明显,而且很快就失败了。将两个用户的 save() 更改为 save(failOnError: true),它应该会显示缺少的内容。 是的,这是一些验证错误,非常感谢您的支持,顺便说一下,您是否有任何有用和有用的 Grails 资源来给我很好的推动,因为我是经验丰富的 Java 开发人员,但我是新手到 Grails,我需要用它完成一个小项目。 参见grails.org/wiki/Books 的书籍 - “Grails 2 权威指南”和“Grails in Action”是最全面的

以上是关于使用 Spring Security Grails 插件编码密码的主要内容,如果未能解决你的问题,请参考以下文章

Grails - grails-spring-security-rest - 无法从 application.yml 加载 jwt 机密

使用 Spring Security Grails 插件编码密码

Grails + spring-security-core:用户登录后如何分配角色?

在 Grails 中使用 Pre/Post Spring-Security Annotations

Grails 3.0 和 Spring Security

Grails - 卸载 Spring Security Core