Как заставить Play Framework 2 всегда использовать SSL?
У меня есть приложение Play Framework, работающее на Heroku, используя конечную точку SSL Heroku.
Я хочу сделать все страницы доступными только через SSL
.
Какой лучший способ для этого?
До сих пор лучшим решением было использовать onRouteRequest
в моих GlobalSettings
и маршрутных non-SSL
запросах для специального обработчика перенаправления:
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
? Или что-то еще лучше?
Ответы
Ответ 1
Мы сделали так, как вы, но с фильтром воспроизведения, который генерирует MovedPermanently вместо метода контроллера.
Я не думаю, что есть лучший способ с героку, или, по крайней мере, мы не смогли найти какую-либо функцию для отключения незашифрованного HTTP.
Ответ 2
Вот еще один способ использования фильтра. Это также использует строгую безопасность транспорта, чтобы гарантировать, что будущие запросы перейдут на 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)
}
}
}
Ответ 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);
}
Ответ 4
В Play 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"
И обратите внимание, что если у вас включен обработчик запросов (найдите play.http.requestHandler
внутри файла application.conf
), фильтры не будут работать, я предлагаю обрабатывать запросы с использованием фильтров и удалять ваш текущий запросHandler.
Ответ 5
У меня было аналогичное требование, используя 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);
}
}
Это не обрабатывает пользовательские порты, поэтому некоторые реализации могут потребовать немного дополнительного кодирования.