2013-06-27 2 views
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 так, что он бросает AuthorizationFailedRejection с в запросе не token?

ответ

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 
      } 
      } 
     } 
     } 
    } 

Если пользователь не вошел в систему, то он выдает отказ которым обрабатывается seperatly:

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 
} 
+0

+1. хороший информация. У меня вопрос, потому что я не так хорош в аэрозоле. Что я должен использовать как 'headerValue' (что это значит)? – boburShox

+0

@boburShox, когда вы получаете новый запрос, 'headerValue' помогает вам извлечь значение из некоторого заголовка, если он существует, он принимает некоторую функцию, например,« partialFunction »для извлечения стиля шаблона:' { case HttpHeader (lowerCaseName , значение) ⇒ Некоторые (значение) case _ ⇒ None } '. И с этой функцией, если такой заголовок существует, возвращает вам необязательное значение. – 4lex1v

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) 
    } 
}) 

Я надеюсь, что это поможет другим людям.

+1

На самом деле это не хороший дизайн. И вы путаете аутентификацию и авторизацию. Его лучше удалить patmat из вашей директивы и вернуть вариант | отклонить – 4lex1v