Play Framework:如何处理api密钥

时间:2014-06-01 20:49:32

标签: scala playframework api-key

我已经实现了一个REST API,它使用JWT(Json Web Token)处理用户授权,并且工作正常。现在,我想知道如何处理API密钥以防止未经授权的应用程序使用我的API。

我们的想法是在请求标头中添加API密钥,并为其实现自定义操作构建器。现在的问题是:由于我已经有一个处理授权令牌的自定义操作构建器,我该如何集成新的自定义操作构建器?

假设这是我的JWT自定义动作构建器......

class ApiRequest[A](
  val token: Token,
  request: Request[A]) extends WrappedRequest[A](request) {

  ...
}

class SecuredAction extends ActionBuilder[ApiRequest] {

  def invokeBlock[A](request: Request[A], block: (ApiRequest[A]) => Future[SimpleResult]) = {
  ...
  }
}

...这是我的api_key自定义动作构建器

class ApiKeyRequest[A](
  val apiKey: String,
  request: Request[A]) extends WrappedRequest[A](request) {

  ...
}

class ApiKeyAction extends ActionBuilder[ApiKeyRequest] {

  def invokeBlock[A](request: Request[A], block: (ApiKeyRequest[A]) => Future[SimpleResult]) = {
  ...
  }
}

...如何在我的控制器中一起使用它们?

object MyController extends Controller {

  // here I need to compose SecuredAction with ApiKeyAction...
  def doSomething = SecuredAction.async { implicit request => 
    ...
  }
}

感谢。

1 个答案:

答案 0 :(得分:2)

查看Action Composition,它允许您检查和转换操作请求。如果您使用播放过滤器,那么它将在每个请求上运行。

例如,您可以创建一个检查请求的TokenAction,如果找到了令牌,则优化请求以包含基于令牌的信息,例如用户。如果没有找到令牌,则返回另一个结果,如Unauthorized,Redirect或Forbidden。

我创建了一个SessionRequest,它具有一个带有可选登录用户的用户属性,它首先从数据库中查找现有会话,然后获取附加用户并将其传递给请求

如果没有可用的用户,则过滤器(WithUser)将拦截SessionRequest,然后将用户重定向到登录页面

// Request which optionally has a user
class SessionRequest[A](val user: Option[User], request: Request[A]) extends WrappedRequest[A](request)

object SessionAction extends ActionBuilder[SessionRequest] with ActionTransformer[Request, SessionRequest] {

  def transform[A](request: Request[A]): Future[SessionRequest[A]] = Future.successful {
    val optionalJsonRequest: Option[Request[AnyContent]] = request match {
      case r: Request[AnyContent] => Some(r)
      case _ => None
    }

    val result = {
      // Check if token is in JSON request
      for {
        jsonRequest <- optionalJsonRequest
        json <- jsonRequest.body.asJson
        sessionToken <- (json \ "auth" \ "session").asOpt[String]
        session <- SessionRepository.findByToken(sessionToken)
      } yield session
    } orElse {
      // Else check if the token is in a cookie
      for {
        cookie <- request.cookies.get("sessionid")
        sessionToken = cookie.value
        session <- SessionRepository.findByToken(sessionToken)
      } yield session
    } orElse {
      // Else check if its added in the header
      for {
        header <- request.headers.get("sessionid")
        session <- SessionRepository.findByToken(header)
      } yield session
    }

    result.map(x => new SessionRequest(x.user, request)).getOrElse(new SessionRequest(None, request))
  }
}

// Redirect the request if there is no user attached to the request
object WithUser extends ActionFilter[SessionRequest] {
  def filter[A](request: SessionRequest[A]): Future[Option[Result]] = Future.successful {
    request.user.map(x => None).getOrElse(Some(Redirect("http://website/loginpage")))
  }
}

然后您可以在动作中使用它

def index = (SessionAction andThen WithUser) { request =>
  val user = request.user
  Ok("Hello " + user.name)
}

我希望这能让您了解如何使用动作合成