Scala 使用基本身份验证和SSL播放框架REST

Scala 使用基本身份验证和SSL播放框架REST,scala,playframework,playframework-2.0,scala-collections,typesafe-activator,Scala,Playframework,Playframework 2.0,Scala Collections,Typesafe Activator,我是这个认证领域的新手。我搜索了很多,但找不到一种方法来验证对Play服务器的其余调用。各种方法和最佳实践是什么?阅读以下自述文件/文章:并查看相应的示例应用程序(同一链接)。 它解释了如何满足您的要求。对于Scala,可能是最好的可设置解决方案。您将在给定的链接中找到大量文档和示例。 您还可以查看另一个有效选项 你们会在清单上找到更多的可能性 如果您想/需要烘焙自己的解决方案,研究现有解决方案的代码以获取灵感和想法可能仍然很有用。尽管如此,我对任何与安全相关的东西的一般建议是,除非您真的需要它

我是这个认证领域的新手。我搜索了很多,但找不到一种方法来验证对Play服务器的其余调用。各种方法和最佳实践是什么?

阅读以下自述文件/文章:并查看相应的示例应用程序(同一链接)。 它解释了如何满足您的要求。

对于Scala,可能是最好的可设置解决方案。您将在给定的链接中找到大量文档和示例。 您还可以查看另一个有效选项

你们会在清单上找到更多的可能性

如果您想/需要烘焙自己的解决方案,研究现有解决方案的代码以获取灵感和想法可能仍然很有用。尽管如此,我对任何与安全相关的东西的一般建议是,除非您真的需要它(和/或真正知道您在做什么),否则不要自己实现它


顺便说一句,这里的REST绝对没有什么特别的。实际上,您是在保护控制器方法,因此它们的调用是否由REST调用触发并不重要。

一种非常简单的方法是使用动作合成。有关示例,请查看Guillaume Bort提供的以下要点:。如果要在异步操作中使用它,可以编写如下内容:

def BasicSecured[A](username: String, password: String)(action: Action[A]): Action[A] = Action.async(action.parser) { request =>
  request.headers.get("Authorization").flatMap { authorization =>
    authorization.split(" ").drop(1).headOption.filter { encoded =>
      new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match {
        case u :: p :: Nil if u == username && password == p => true
        case _ => false
      }
    }
  }.map(_ => action(request)).getOrElse {
    Future.successful(Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured Area""""))
  }
}

SSL与基本身份验证无关。您可以直接或通过前端HTTP服务器(如ngnix)将HTTPS用于API。播放文档中有关于这个主题的非常详细的信息。

如果我们只是讨论基本auth,您不需要任何外部模块。基本上,您可以使用

这是一个完整的例子

如果您还需要授权,您可以简单地将前面的示例与。它将允许您提供对某些客户端组的访问,并拒绝对其他客户端组的访问


SSL支持与身份验证无关。但是,如

中所述,也可以使用过滤器。以下内容基于第2.5节

import org.apache.commons.codec.binary.Base64

override def apply(nextFilter: RequestHeader => Future[Result])
                (requestHeader: RequestHeader): Future[Result] = {

val auth = requestHeader.headers.get("Authorization")
val invalidResult = Future.successful(
    Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured"""")
  )

if (auth.isEmpty) {
  invalidResult
}
else {
  val credentials = new String(Base64.decodeBase64(auth.get.split(" ").drop(1).head.getBytes)).split(":")

  if (credentials.length < 2) {
    invalidResult
  }
  else {
    for {
      authVerify <- verify(credentials(0), credentials(1))
      r <- {
        if (authVerify) {
          nextFilter(requestHeader).map { result: Result => result }
        }
        else {
          invalidResult
        }
      }
    } yield {
      r
    }
  }
} 
}

def verify(username: String, password: String): Future[Boolean]
import org.apache.commons.codec.binary.Base64
覆盖def apply(nextFilter:RequestHeader=>Future[Result])
(requestHeader:requestHeader):未来[结果]={
val auth=requestHeader.headers.get(“授权”)
val invalidResult=Future.successful(
未经授权。WithHeader(“WWW-Authenticate”->“Basic-realm=”Secured“”)
)
如果(授权为空){
无效结果
}
否则{
val凭证=新字符串(Base64.decodeBase64(auth.get.split(“”).drop(1.head.getBytes)).split(“”):“”)
如果(长度<2){
无效结果
}
否则{
为了{

authVerify基本上,我从@centr获得了答案,并试图使其更具可读性。看看您是否喜欢相同代码的此版本。经过彻底测试,效果如预期

def BasicSecured[A](username: String, password: String)(action: Action[A]): Action[A] = Action.async(action.parser) { request =>
    val submittedCredentials: Option[List[String]] = for {
      authHeader <- request.headers.get("Authorization")
      parts <- authHeader.split(' ').drop(1).headOption
    } yield new String(decodeBase64(parts.getBytes)).split(':').toList

    submittedCredentials.collect {
      case u :: p :: Nil if u == username && p == password => action(request)
    }.getOrElse {
      Future.successful(Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured Area""""))
    }
  }
def BasicSecured[A](用户名:String,密码:String)(action:action[A]):action[A]=action.async(action.parser){request=>
val submittedCredentials:选项[列表[字符串]]=for{
authHeader“”Basic realm=“安全区域”“”“))
}
}

我在单独的答案中添加了一份重新编写的代码副本。谢谢,这项工作的机制与预期的一样好。我不喜欢使用“”和“:”在字符串拆分中,作为简单的字符分隔符就足够了,可以避免正则表达式开销。此外,使用
collect
方法,带谓词的筛选器在单行上更整洁。重写的主要动机是随着代码嵌套越来越深,令人讨厌的三角形空格。这需要理解:-)密码第i个
将被拆分多次并拒绝。