【问题标题】:How to force Play framework 2 to always use SSL?如何强制 Play framework 2 始终使用 SSL?
【发布时间】:2013-10-09 10:34:18
【问题描述】:

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

我想让所有页面仅通过SSL 提供。

最好的方法是什么?

到目前为止,我最好的解决方案是在我的GlobalSettings 中使用onRouteRequest 并将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 内完成这项工作?或者更好的东西?

【问题讨论】:

  • 如果您正在寻找的话,您可以从 -Dhttp.port=disabled 开始关闭非 ssl:playframework.com/documentation/2.2.x/ConfiguringHttps
  • 从 Play 的角度来看,我没有使用 SSL。 Heroku 的负载均衡器正在将 SSL 代理到应用程序的直接 HTTP。
  • 这方面有更新吗?我最终做了类似的事情,但我更喜欢禁用的 http,但我无法让它工作。

标签: scala ssl heroku playframework-2.0


【解决方案1】:

这是使用过滤器的另一种方式。这还使用严格的传输安全性来确保未来的请求转到 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)
        }
    }
}

【讨论】:

  • 为什么添加result.withHeaders(("Strict-Transport-Security", "max-age=31536000"))?这是强制标头吗?
  • 您可以在my blog了解更多信息
【解决方案2】:

我们所做的与您非常相似,但使用了一个生成 MovedPermanently 而不是控制器方法的播放过滤器。

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

【讨论】:

  • 如何强制从过滤器重定向?
  • 只返回一个结果(或 play 2.2 中的 Future[SimpleResult]),而不是调用下一个动作/基本动作
【解决方案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);
}

【讨论】:

  • 不幸的是,我在 X-Forwarded-Proto 中没有得到任何东西。可能是由于前端 HTTP 服务器设置所致。
  • 这不能作为控制器工作。Default.redirect() 是一个非静态方法。
【解决方案4】:

在 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"

请注意,如果您启用了请求处理程序(在 application.conf 文件中查找 play.http.requestHandler),过滤器将不起作用,我建议使用过滤器处理请求并删除您当前的 requestHandler。

【讨论】:

    【解决方案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);
    
      }
    }
    

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

    【讨论】:

      猜你喜欢
      • 2014-12-20
      • 1970-01-01
      • 2012-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-25
      • 1970-01-01
      • 2019-07-28
      相关资源
      最近更新 更多