我的服务的所有API调用都是HTTP POST,参数在多部分正文中传递。目前我的身份验证看起来像这样
formField("token".as[String]) { token =>
authorize(isAuthorized(token)) {
.... my other routes here
}
}
但它看起来太冗长了。理想情况下,我希望有类似的东西:
myAuthorization {
.... my other routes here
}
所以我这样写:
def myAuthorization: Directive0 =
formField("token".as[String]).require(isAuthorized(_))
但您如何撰写myAuthorization
,以便在请求中抛出AuthorizationFailedRejection
但没有token
?
答案 0 :(得分:6)
我喜欢用提取器编写喷雾指令。我的一个项目的简化示例:
def loggedInUser: Directive[Option[User] :: HNil] = headerValue {
case Cookie(cookies) => cookies.find(_.name === usrCookie) ∘ extractUserFromCookie
case _ => None
} | reject(NoUserLoggedInRejection)
def authOpt(usrOpt: Option[User], usr: String): Directive0 =
authorize(usrOpt.filter(_.login ≠ usr).isEmpty)
然后在您的路由文件中,您可以使用提取器:
pathPrefix("path") {
loggedInUser { user =>
authOpt(user, Clients.client) {
path("path") {
get {
complete {
html.page() // i'm using Twirl as a fe engine
}
}
}
}
}
如果没有用户登录,那么它会抛出一个单独处理的拒绝:
implicit val customRejectionHandlers = RejectionHandler {
case NoUserLoggedInRejection :: _ =>
ctx => ctx.redirect("/auth", StatusCodes.SeeOther)
}
在安全示例的情况下,这不是最好的,但我认为这很明显
<强>加强>
评论中的headerValue
指令示例:
// Lets define a extractor for the Host header from the request
val hostExtractor: HttpHeader => Option[String] = {
case Host(host, port) => Some(host)
case _ => None
}
// Then some path
val route = {
path("somePath") {
post {
headerValue(hostExtractor) { host =>
complete(host)
}
}
}
}
大多数此类指令都是通过extract
指令实现的,该指令采用类型为RequestContext ⇒ T
的函数并返回T
。例如headerValue
也通过extract
实现:
extract(_.request.headers.mapFind(protectedF)).flatMap {
case Some(Right(a)) ⇒ provide(a)
case Some(Left(rejection)) ⇒ reject(rejection)
case None ⇒ reject
}
答案 1 :(得分:0)
实际上我意识到丢失表单字段错误可能会误导客户端。如果在数据库中找不到给定的令牌,这就是我如何返回auth拒绝
def myAuthorization = formField(FieldDefMagnet("token".as[String])).flatMap[User :: HNil]((token: String) => {
getUserByToken(token) match {
case Some(user) => provide(user)
case None => reject(AuthorizationFailedRejection)
}
})
我希望它可以帮助其他人。