【问题标题】:grails 2.5 how to validate domain object password with service inside validatorgrails 2.5如何使用验证器内的服务验证域对象密码
【发布时间】: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


【解决方案1】:

您可以使用这两种方法来完成您的任务。

1:在 encodePassword() 中进行验证:不抛出异常,而是向实例添加错误。我认为您的encodePassword() 函数在您的同一个域中,因此使用this.errors 获取与其关联的错误对象。例如:

this.errors.rejectValue("password", "user.password.pattern.error")

有不同的rejectValue方法,这个接受你的message.properties文件中定义的字段名和消息代码。

2:自定义验证器

isDirty() 不是静态方法,使用自定义验证器中提供的 obj 调用它。 isDirty() 接受要检查是否脏的属性名称而不是其值。

obj.isDirty(PropertyName)

constraints 是一个静态块,它不能直接访问你的服务。您需要使用静态上下文注入服务。

static SiteService siteService;

我建议使用自定义验证器。

【讨论】:

  • 我尝试了自定义验证器路由,但这失败了,因为当密码在 beforeInsert 方法中的编码密码方法中编码时,它会默默地失败验证并且不保存更新的密码而不抛出任何类型的错误。
  • 根据你想在哪里检查验证,你需要决定在那里调用编码密码方法
  • 在Validator里做编码?有趣的想法。这是一种有效的方法吗?
  • 对不起,我不是要在验证器中编码密码。如果您使用的是自定义验证器,并且在 beforeInsert 中失败,那么这意味着验证器无法正常工作。
猜你喜欢
  • 2011-07-03
  • 1970-01-01
  • 2018-02-17
  • 1970-01-01
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多