使用 Spring Security Grails 插件对密码进行编码

Encoding a password with the Spring Security Grails plugin

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

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

这是我的 BootStrap :

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 = {
    }
}

有什么我想念的吗?

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

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

因此您的代码应如下所示:

def springSecurityService

def init = { servletContext ->
    ...
}

但是你的代码会遇到第二个问题。假设您没有更改 User class 中的自动散列逻辑,您将编码两次。一次在 BootStrap,一次在域 class。如果您这样做,则没有人能够进行身份验证。选择一个 - 像您正在做的那样明确散列并从域 class 中删除代码,或者保留域 class 代码并仅设置明文密码。如果您选择选项 #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() }
    }
}

更好的方法是将此代码移至事务服务。这留作 reader.

的练习