【发布时间】:2015-09-15 12:50:29
【问题描述】:
我们有一个简单的 operator 对象,它使用 spring security 对密码进行编码:
class Operator
transient springSecurityService
def siteService
String username
Site site
String password
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
现在我们要验证密码是否与运行时定义的正则表达式匹配。可以通过 UI(即标准 CRUD 表单)创建运算符。我们对每个站点都有不同的正则表达式。密码被编码的密码覆盖,我们不应该对其进行测试,这一事实使其更具挑战性。
尝试1:在encodePassword()中进行验证:
def beforeInsert() {
protected void encodePassword() {
String regexp = siteService.getInheritedValue(site, "operatorPasswordRegexp")
if (!password.matches(regexp) {
thrown new RuntimeException "invalid password format"
}
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
}
这部分有效,因为它会停止创建与运行时发现的正则表达式不匹配的密码。问题是当我们希望操作员编辑表单以友好的验证消息突出显示密码字段时,它会引发异常,生成 500 错误页面。
尝试二,使用自定义验证器
static constraints = {
password password: true, blank:false, validator: { val, obj ->
if (obj.isDirty(val)) {
return true
}
String regexp = obj.siteService.getInheritedValue(obj.operates, "operatorPasswordRegexp")
if (regexp != null && regexp != "") {
return val.matches(regexp)
}
return true
}
这似乎有效,但保存总是默默地失败。我花了一些时间才意识到为什么 - 当你这样做时:
operator.password="valid1"
opertor.save(failonError:true)
不会抛出任何错误。即使您删除了 failonError,并检查了返回值,它也始终为 null(无错误)。但这并不能拯救操作员。
问题在于 beforeInsert 正在将密码更新为编码版本,该版本当然不会通过验证器(并且不应该),验证器此时说不,并且保存默默地失败。 IE。验证器被调用两次以进行一次保存。
问题是,如何让 beforeInsert() 代码不调用验证器,或让验证器忽略从 beforeInsert 调用?
【问题讨论】:
-
你试过
obj.isDirty('password')和obj.siteService...吗?
标签: regex validation grails