3

我的服务的所有 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以便它在请求中抛出AuthorizationFailedRejectionno呢?token

4

2 回答 2

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指令实现的,它接受一个 type: 的函数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
}
于 2013-06-28T09:27:11.033 回答
0

实际上,我意识到缺少表单字段错误可能会误导客户。如果在数据库中找不到给定的令牌,这就是我如何返回身份验证拒绝

def myAuthorization = formField(FieldDefMagnet("token".as[String])).flatMap[User :: HNil]((token: String) => {
  getUserByToken(token) match {
    case Some(user) => provide(user)
    case None => reject(AuthorizationFailedRejection)
  }
})

我希望它可以帮助其他人。

于 2013-06-27T23:57:36.070 回答