我已经实现了一个 REST API,它使用 JWT(Json Web token )处理用户授权,并且工作正常。现在我想知道如何处理 API key 以防止未经授权的应用程序使用我的 API。
这个想法是在请求 header 中添加 API key 并为其实现自定义操作构建器。现在的问题是:由于我已经有一个处理授权 token 的自定义操作构建器,我该如何集成新的自定义操作构建器?
假设这是我的 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]) = {
...
}
}
...我如何在我的 Controller 中一起使用它们?
object MyController extends Controller {
// here I need to compose SecuredAction with ApiKeyAction...
def doSomething = SecuredAction.async { implicit request =>
...
}
}
谢谢。
最佳答案
看看Action Composition ,它允许您检查和转换对操作的请求。如果您使用 Play 过滤器,那么它将在每次请求时运行。
例如,您可以创建一个 TokenAction 来检查请求,如果找到了 token ,则优化请求以包含基于 token 的信息,例如用户。如果没有找到 token ,则返回另一个结果,如未授权、重定向或禁止。
我创建了一个 SessionRequest ,它有一个带有可选登录用户的用户属性,它首先从数据库中查找一个现有的 session ,然后获取附加的用户并将其传递给请求
然后过滤器(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")))
}
}
然后你可以在一个 Action 上使用它
def index = (SessionAction andThen WithUser) { request =>
val user = request.user
Ok("Hello " + user.name)
}
我希望这会给你一个关于如何使用 Action 合成的想法
关于scala - Play Framework : How to deal with api keys,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23984334/