2013-10-02 16 views
16

Ho un'app Play Framework in esecuzione su Heroku, utilizzando l'endpoint SSL di Heroku.Come forzare Play framework 2 a usare sempre SSL?

Desidero rendere tutte le pagine disponibili solo tramite SSL.

Qual è il modo migliore per farlo?

Finora, la mia soluzione migliore è quella di utilizzare onRouteRequest nei miei GlobalSettings e percorso non-SSL richieste di uno speciale gestore di reindirizzamento:

override def onRouteRequest(request: RequestHeader): Option[Handler] = { 
    if (Play.isProd && !request.headers.get("x-forwarded-proto").getOrElse("").contains("https")) { 
    Some(controllers.Secure.redirect) 
    } else { 
    super.onRouteRequest(request) 
    } 
} 

e

package controllers 

import play.api.mvc._ 

object Secure extends Controller { 

    def redirect = Action { implicit request => 
    MovedPermanently("https://" + request.host + request.uri) 
    } 
} 

C'è un modo per fare questo interamente da GlobalSettings? O qualcosa di ancora migliore?

+2

È possibile disattivare non-ssl iniziando con -Dhttp.port = disabilitato se è quello che stai cercando: http://www.playframework.com/documentation/2.2.x/ConfiguringHttps –

+1

Dal punto di vista di Play, non sto usando SSL. Il bilanciamento del carico di Heroku sta trasmettendo il protocollo SSL a HTTP diretto per l'app. –

+0

c'è stato qualche aggiornamento su questo? Ho finito per fare qualcosa di simile ma avrei preferito l'http disabilitato ma non riuscivo a farlo funzionare. – Setheron

risposta

4

Abbiamo fatto così tanto come te ma con un filtro di riproduzione che genera un MovedPermanently anziché un metodo di controller.

Non penso che ci sia un modo migliore con heroku, o almeno non siamo riusciti a trovare alcuna funzione per disabilitare HTTP non criptato.

+0

Come si forza un reindirizzamento da un filtro? –

+2

Basta restituire un risultato (o Future [SimpleResult] in riproduzione 2.2) invece di richiamare l'azione successiva/essentialaction – johanandren

3

Ecco la soluzione per la versione Java di Play Framework.

Aggiungere il seguente al file Global.java:

@Override 
public Handler onRouteRequest(RequestHeader request) { 
    String[] x = request.headers().get("X-Forwarded-Proto"); 
    if (Play.isProd() && (x == null || x.length == 0 || x[0] == null || !x[0].contains("https"))) 
     return controllers.Default.redirect("https://" + request.host() + request.uri()); 
    return super.onRouteRequest(request); 
} 
+0

Purtroppo, non sto ottenendo nulla in X-Forwarded-Proto. Probabilmente a causa della configurazione del server HTTP frontale. –

12

Ecco un altro modo per mezzo del filtro. Questo utilizza anche una rigorosa sicurezza di trasporto per garantire che le richieste future vadano a https.

object HTTPSRedirectFilter extends Filter with Logging { 

    def apply(nextFilter: (RequestHeader) => Future[SimpleResult])(requestHeader: RequestHeader): Future[SimpleResult] = { 
     //play uses lower case headers. 
     requestHeader.headers.get("x-forwarded-proto") match { 
      case Some(header) => { 
       if ("https" == header) { 
        nextFilter(requestHeader).map { result => 
         result.withHeaders(("Strict-Transport-Security", "max-age=31536000")) 
        } 
       } else { 
        Future.successful(Results.Redirect("https://" + requestHeader.host + requestHeader.uri, 301)) 
       } 
      } 
      case None => nextFilter(requestHeader) 
     } 
    } 
} 
+0

Perché hai aggiunto il 'result.withHeaders ((" Strict-Transport-Security "," max-age = 31536000 "))'? È un'intestazione obbligatoria? – irundaia

+0

Puoi leggere di più su [il mio blog] (http://www.mentful.com/2014/05/25/play-framework-filter-for-aws-elastic-load-balancer-forward-http-to -https /) – Dimitry

0

Avevo un requisito simile utilizzando Play 2.2.3. Sto eseguendo la mia applicazione dietro un load balancer che sta facendo la terminazione SSL, e volevo che tutte le richieste HTTP venissero reindirizzate su SSL. Nel mio caso, il mio load balancer (Amazon ELB) aggiunge l'intestazione X-Forwarded-Proto, quindi l'ho cancellato come segue. In Global.java (o qualunque classe che hai che si estende GlobalSettings), aggiungere questo metodo:

@SuppressWarnings("rawtypes") 
@Override 
public Action onRequest(Request actionRequest, Method actionMethod) { 

    String forwardedProtocol = actionRequest.getHeader("X-Forwarded-Proto"); 

    if (StringUtils.equalsIgnoreCase(forwardedProtocol, "http")) { 

    // Redirect to HTTPS 
    final String secureURL = "https://" + actionRequest.host() + actionRequest.uri(); 
    return new Action.Simple() { 
     @Override 
     public Promise<play.mvc.SimpleResult> call(Context ctx) throws Throwable { 
     return Promise.pure(Results.movedPermanently(secureURL)); 
     } 
    }; 

    } else { 

    return super.onRequest(actionRequest, actionMethod); 

    } 
} 

Questo non gestisce porte personalizzate, in modo da alcune implementazioni possono potrebbe richiedere un po 'di codifica supplementare.

2

In Play 2.5.x del Java (penso che dovrebbe funzionare in gioco 2.4.x così ma utilizzando Promise.F invece di CompletionStage), ho aggiunto un filtro:

public class TLSFilter extends Filter { 
    @Inject 
    public TLSFilter(Materializer mat) { 
     super(mat); 
    } 

    @Override 
    public CompletionStage<Result> apply(Function<Http.RequestHeader, CompletionStage<Result>> next, Http.RequestHeader rh) { 
     if (Play.current().isProd()) { 
      String[] httpsHeader = rh.headers().getOrDefault(Http.HeaderNames.X_FORWARDED_PROTO, new String[]{"http"}); 
      if (Strings.isNullOrEmpty(httpsHeader[0]) || httpsHeader[0].equalsIgnoreCase("http")) { 
       return CompletableFuture.completedFuture(Results.movedPermanently("https://".concat(rh.host().concat(rh.uri())))); 
      } 
     } 
     return next.apply(rh).toCompletableFuture(); 
    } 
} 

E poi aggiungerlo alla lista dei filtri per usarlo:

public class AppFilters extends DefaultHttpFilters { 

    @Inject 
    public AppFilters(TLSFilter tlsFilter, GzipFilter gzipFilter) { 
     super(tlsFilter, gzipFilter); 
    } 
} 

E poi di utilizzare i filtri aggiungere il seguente all'interno application.conf:

play.http.filters = "filters.AppFilters" 

E si noti che se si dispone di un gestore richieste abilitato (cercare play.http.requestHandler all'interno del file application.conf), i filtri non funzioneranno, suggerisco di gestire le richieste utilizzando i filtri e rimuovere la richiesta correnteHandler.

Problemi correlati