16

我有一个使用 Heroku 的 SSL 端点在 Heroku 上运行的 Play Framework 应用程序。

我想让所有页面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?或者更好的东西?

4

5 回答 5

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)
        }
    }
}
于 2014-05-19T13:43:33.233 回答
4

我们已经完成了与您类似的操作,但使用了一个生成 MovedPermanently 而不是控制器方法的播放过滤器。

我认为 heroku 没有更好的方法,或者至少我们找不到任何禁用未加密 HTTP 的功能。

于 2013-10-04T12:49:45.753 回答
2

这是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);
}
于 2014-03-18T19:49:44.480 回答
2

在 Play 2.5.x Java 中(我认为也应该在 play 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.requestHandlerapplication.conf文件中查找),过滤器将不起作用,我建议使用过滤器处理请求并删除您当前的 requestHandler。

于 2017-01-03T08:12:01.403 回答
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);

  }
}

这不处理自定义端口,因此某些实现可能需要一些额外的编码。

于 2014-07-16T09:23:49.487 回答