Antwort

10

Ein sehr einfacher Weg ist die Verwendung von Action Composition. Werfen Sie einen Blick auf diesen von Guillaume Bort zur Verfügung gestellten Gist: https://gist.github.com/guillaumebort/2328236. Wenn Sie es in einem Asynchron-Aktion verwenden möchten, können Sie etwas schreiben wie:

def BasicSecured[A](username: String, password: String)(action: Action[A]): Action[A] = Action.async(action.parser) { request => 
    request.headers.get("Authorization").flatMap { authorization => 
    authorization.split(" ").drop(1).headOption.filter { encoded => 
     new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match { 
     case u :: p :: Nil if u == username && password == p => true 
     case _ => false 
     } 
    } 
    }.map(_ => action(request)).getOrElse { 
    Future.successful(Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured Area"""")) 
    } 
} 

SSL nichts mit Standardauthentifizierung zu tun haben. Sie können HTTPS für API entweder direkt oder über einen Front-End-HTTP-Server wie ngnix verwenden. Es gibt ziemlich gute Details in der Play-Dokumentation zu diesem Thema.

+0

Ich habe eine überarbeitete Kopie dieses Codes in eine separate Antwort hinzugefügt. Danke, die Mechanik funktioniert gut wie erwartet. Ich mag die Verwendung von "" und ":" in String-Splits nicht, da einfache Zeichentrennzeichen ausreichen würden, um Regex-Overhead zu vermeiden. Auch der Filter mit Prädikat ist in einer einzelnen Zeile mit 'collect' Methode ordentlicher.Die Hauptmotivation für das Neuschreiben war der ärgerliche dreieckige Whitespace, da der Code tiefer und tiefer verschachtelt wird. Dies erforderte ein Verständnis :-) –

+0

Passwörter mit ':' würden mehrfach geteilt und zurückgewiesen. – Niklas

2

Für Scala ist Secure Social wahrscheinlich die beste etablierte Lösung. Unter dem angegebenen Link finden Sie zahlreiche Dokumentationen und Beispiele. Sie können auch eine andere gültige Option Play2-auth betrachten.

Sie finden noch mehr Möglichkeiten auf Play 2 Modules Liste.

Wenn Sie Ihre eigene Lösung backen wollen/müssen, wird es wahrscheinlich immer noch nützlich sein, den Code bestehender Lösungen nach Inspiration und Ideen zu durchsuchen. Nichtsdestotrotz, mein allgemeiner Ratschlag in Bezug auf alles, was mit Sicherheit zu tun hat, ist NICHT, es selbst zu implementieren, es sei denn, Sie benötigen es wirklich (und/oder wissen wirklich, was Sie tun).

BTW, hier gibt es absolut nichts Spezifisches an REST. Sie schützen im Wesentlichen Ihre Controller-Methoden, so dass es keine Rolle spielt, ob ihr Aufruf von einem REST-Aufruf ausgelöst wurde oder nicht.

3

Wenn wir nur über grundlegende Auth sprechen, benötigen Sie kein externes Modul. Grundsätzlich könnten Sie es mit action composition implementieren.

Here ist ein vollständiges Beispiel darüber. Wenn Sie auch Autorisierung benötigen, können Sie das vorherige Beispiel einfach mit Deadbolt kombinieren. Es ermöglicht Ihnen, Zugriff auf einige Gruppen von Clients bereitzustellen und anderen den Zugriff zu verweigern.

SSL-Unterstützung hat nichts mit der Authentifizierung zu tun. Dies ist jedoch in der Play Documentation

2

erläutert. Ein Filter könnte ebenfalls verwendet werden. Das Folgende basiert auf Play 2.5.

import org.apache.commons.codec.binary.Base64 

override def apply(nextFilter: RequestHeader => Future[Result]) 
       (requestHeader: RequestHeader): Future[Result] = { 

val auth = requestHeader.headers.get("Authorization") 
val invalidResult = Future.successful(
    Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured"""") 
) 

if (auth.isEmpty) { 
    invalidResult 
} 
else { 
    val credentials = new String(Base64.decodeBase64(auth.get.split(" ").drop(1).head.getBytes)).split(":") 

    if (credentials.length < 2) { 
    invalidResult 
    } 
    else { 
    for { 
     authVerify <- verify(credentials(0), credentials(1)) 
     r <- { 
     if (authVerify) { 
      nextFilter(requestHeader).map { result: Result => result } 
     } 
     else { 
      invalidResult 
     } 
     } 
    } yield { 
     r 
    } 
    } 
} 
} 

def verify(username: String, password: String): Future[Boolean] 
3

grundsätzlich habe ich die Antwort von @centr genommen und versucht, es ein wenig lesbarer zu machen. Sehen Sie, ob Sie diese Version des gleichen Codes bevorzugen. Gründlich getestet, funktioniert wie erwartet.

def BasicSecured[A](username: String, password: String)(action: Action[A]): Action[A] = Action.async(action.parser) { request => 
    val submittedCredentials: Option[List[String]] = for { 
     authHeader <- request.headers.get("Authorization") 
     parts <- authHeader.split(' ').drop(1).headOption 
    } yield new String(decodeBase64(parts.getBytes)).split(':').toList 

    submittedCredentials.collect { 
     case u :: p :: Nil if u == username && p == password => action(request) 
    }.getOrElse { 
     Future.successful(Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured Area"""")) 
    } 
    }