我们有一个简单的操作符对象,它使用spring安全性对密码进行编码:
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
}
现在我们要验证密码是否与运行时定义的regexp匹配。操作符可以通过UI(即标准CRUD表单)创建。每个站点都有不同的regexp。事实上,密码会被一个编码的密码覆盖,而我们不应该测试它,这使得它更具挑战性。
尝试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
}
}
这部分有效,因为它阻止创建与运行时发现的regexp不匹配的密码。问题是,当我们希望操作员编辑表单用友好的验证消息突出显示密码字段时,它会抛出一个异常,生成一个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,并检查返回值,它始终为空(没有错误)。但是它不保存操作符。
问题在于beforeInsert将密码更新为一个编码版本,而这个版本当然没有通过验证器(也不应该通过),验证器此时说不,并且保存无声地失败。也就是说,对于单个保存,验证器被调用两次。
问题是,我如何得到beforeInsert()代码不调用验证器,或验证器忽略从beforeInsert调用?
您可以使用这两种方法来完成任务。
1:在encodePassword()中进行验证:不抛出异常,而是向实例添加错误。我认为你的encodePassword()
函数是在相同的域中,所以用this.errors
得到与它相关的错误对象。例:
this.errors.rejectValue("password", "user.password.pattern.error")
有不同的rejectValue方法,这个方法接受字段名和消息中定义的消息代码。属性文件。
2:自定义验证器:
isDirty()
不是一个静态方法,使用自定义验证器中提供的obj调用它。isDirty()
接受要检查是否脏的属性名,而不是它的值。
obj.isDirty(PropertyName)
constraints是一个静态块,它不能直接访问你的服务。你需要使用静态上下文注入你的服务。
static SiteService siteService;
我建议使用自定义验证器。