2013-10-02 29 views
16

我有一個使用Heroku的SSL端點在Heroku上運行的Play Framework應用程序。如何強制播放框架2以始終使用SSL?

我想僅通過SSL提供所有頁面。

什麼是最好的方法呢?

到目前爲止,我的最好的解決辦法是使用我GlobalSettings和路線non-SSL請求onRouteRequest到一個特殊的重定向處理:

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

package controllers 

import play.api.mvc._ 

object Secure extends Controller { 

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

有沒有辦法做到這一點完全從GlobalSettings?還是更好的東西?

+2

您可以通過從-Dhttp開始關閉非ssl。端口=禁用,如果這就是你要找的:http://www.playframework.com/documentation/2.2.x/ConfiguringHttps –

+1

從Play的角度來看,我沒有使用SSL。 Heroku的負載均衡器將SSL代理爲應用程序的直接HTTP。 –

+0

有沒有更新?我最終做了類似的事情,但我會偏好禁用http,但是我無法使其工作。 – Setheron

回答

4

我們已經完成了很多與您類似的工作,但使用一個播放過濾器來生成MovedPermanently而不是控制器方法。

我不認爲用heroku有更好的方法,或者至少我們找不到任何功能來禁用未加密的HTTP。

+0

你如何強制從過濾器重定向? –

+2

只要返回一個結果(或未來[SimpleResult]在2.2中)而不是調用下一個動作/ essentialaction – johanandren

3

以下是Java版Play Framework的解決方案。

添加以下到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

不幸的是,我沒有得到X-Forwarded-Proto中的任何東西。可能是由於前面的HTTP服務器設置。 –

12

下面是使用過濾器的另一種方式。這也使用嚴格的傳輸安全性來確保將來的請求轉到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

爲什麼添加'result.withHeaders((「Strict-Transport-Security」,「max-age = 31536000」))?這是一個強制性的標題? – irundaia

+0

你可以在[我的博客]上更多地閱讀它(http://www.mentful.com/2014/05/25/play-framework-filter-for-aws-elastic-load-balancer-forward-http-to -https /) – Dimitry

0

我對Play 2.2.3有類似的要求。我在運行SSL終止的負載均衡器後面運行我的應用程序,並希望所有HTTP請求都被重定向到SSL。在我的情況下,我的負載均衡器(Amazon ELB)添加了X-Forwarded-Proto標頭,所以我按如下方式關閉它。在Global.java(或任何類,你已經得到了擴展GlobalSettings),添加這個方法:

@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); 

    } 
} 

這不處理自定義端口,所以一些可能會在實現可能需要一些額外的編碼。

2

在播放2.5.X的Java(我認爲在比賽的2.4.x應該工作很好,但使用的Promise.F代替CompletionStage),我添加了一個過濾器:

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

然後將其添加到列表中過濾器來使用它:

public class AppFilters extends DefaultHttpFilters { 

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

然後使用您的過濾器添加以下里面application.conf

play.http.filters = "filters.AppFilters" 

請注意,如果您啓用了請求處理程序(在application.conf文件中查找play.http.requestHandler),則過濾器將無法工作,我建議您使用過濾器處理請求並刪除當前的requestHandler。