我正在尝试通过远程身份验证服务对用户进行身份验证。我写了帮助方法,用于向服务发送消息并等待结果:
def authenticateAwait(email: String,
password: String
): Either[String, Option[User]] = {
try {
val future = authenticate(email, password)
Right(Await.result(future, timeout.duration))
} catch {
case _ ⇒ Left("Unable to connect to authentication server")
}
}
如果无法发送消息,或者没有响应,则返回Left[String]
并附带错误说明。如果收到服务响应,则返回Right[Option[User]]
。服务根据身份验证结果以Option[User]
响应。
要执行实际身份验证,我已经使用几个验证器创建了表单,这里是:
val loginForm = Form(
tuple(
"email" → email,
"password" → nonEmptyText
) verifying ("Invalid email or password", result => result match {
case (email, password) ⇒
User.authenticateAwait(email, password) match {
case Left(_) ⇒ true
case Right(optUser) ⇒ optUser.isDefined
}
}) verifying ("Unable to connect to authentication server", result => result match {
case (email, password) ⇒
User.authenticateAwait(email, password) match {
case Left(_) ⇒ false
case Right(optUser) ⇒ true
}
})
)
有一件事让我担心这段代码,它会两次调用authenticateAwait
。这意味着每次验证将发送两条消息。我真正需要的是调用authenticateAwait
一次,存储结果并对其执行各种验证。似乎没有简单的解决方案。
要执行身份验证,访问所需的表单字段,这意味着表单应该绑定然后验证,但是没有办法将错误附加到现有表单(我错了吗?)。
错误只能在创建过程中附加到表单上,因此我应该在验证器中执行验证,但之后会出现上述问题。
我附带的临时解决方案是定义一个方法,并在其中定义var
。
def loginForm = {
var authResponse: Either[String, Option[commons.User]] = null
Form(
tuple(
"email" → email,
"password" → nonEmptyText
) verifying ("Invalid email or password", result ⇒ result match {
case (email, password) ⇒
authResponse = User.authenticateAwait(email, password)
authResponse match {
case Left(_) ⇒ true
case Right(optUser) ⇒ optUser.isDefined
}
}) verifying ("Unable to connect to authentication server", result ⇒ result match {
case (email, password) ⇒
authResponse match {
case Left(_) ⇒ false
case Right(optUser) ⇒ true
}
})
)
}
这显然是一个黑客攻击。有没有更好的解决方案?
更新
在我看来,表单应该只清理输入,但是应该在表单之外执行身份验证。
问题是错误作为Form
的一部分发送到视图,并且不可能将错误附加到现有表单。没有简单的方法来创建带有错误的新表单。
答案 0 :(得分:3)
您必须了解的是Form是不可变的。但是有一个易于使用的实用工具方法来构造一个添加了错误的新表单:
loginForm.copy(errors = Seq(FormError("email", "Already registered")))
答案 1 :(得分:0)
当然,将身份验证与验证混淆只会使操作变得简单。下面是未经测试的,但这是我要进入的方向,正确的投影通过理解过滤。
// point of validation is to sanitize inputs last I checked
val form = Form(tuple("email"→ email, "password"→ nonEmptyText)
val res = for{
case(e,p) <- form.bindFromRequest.toRight("Invalid email or password")
success <- User.authenticateAwait(e,p).right
} yield success
res fold( Conflict(Left(_)), u=> Ok(Right(u)) )