我已经实现了一个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 =>
...
}
}
感谢。
答案 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)
}
我希望这能让您了解如何使用动作合成