具有基本身份验证和 SSL 的 Play Framework REST

Play Framework REST with basic authentication and SSL

我是这个认证领域的新手。我搜索了很多但无法找到一种方法来验证对 Play 服务器的 REST 调用。有哪些不同的方法和最佳实践?

阅读以下 Readme/article:Securing Single Page Apps and REST Services 并查看相应的示例应用程序(相同 link)。 它解释了如何按照您的要求进行操作。

对于 Scala,Secure Social 可能是最好的解决方案。您会在给定的 link 中找到大量文档和示例。 您还可以查看 Play2-auth 作为另一个有效选项。

您会在 Play 2 Modules 列表中找到更多可能性。

如果您 want/need 构建自己的解决方案,查看现有解决方案的代码以获取灵感和想法可能仍然有用。尽管如此,我对与安全相关的任何事情的一般建议是不要自己实施,除非你真的需要它(and/or 真的知道你在做什么)。

顺便说一句,这里绝对没有关于 REST 的具体内容。您实质上是在保护您的控制器方法,因此它们的调用是否由 REST 调用触发并不重要。

一个非常简单的方法是使用 Action Composition。有关示例,请查看 Guillaume Bort 提供的 Gist:https://gist.github.com/guillaumebort/2328236。如果你想在异步操作中使用它,你可以这样写:

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 与基本身份验证没有任何关系。您可以直接或通过 ngnix 等前端 HTTP 服务器为 API 使用 HTTPS。 Play 文档中有关于此主题的非常详细的信息。

如果我们只是谈论 基本身份验证,则不需要任何外部模块。基本上,您可以使用 action composition 来实现它。

Here 是一个完整的例子。

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

SSL 支持与身份验证无关。但是,在Play Documentation

中有说明

也可以使用过滤器。以下基于Play 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]

基本上,我从@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""""))
    }
  }