使用 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.
的练习
在我的 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.
的练习