2011-09-14 78 views
21

我想仅为我的应用程序的登录页面实施https路由。使用播放框架强制执行https路由以登录

Play有可能这样做!不使用前端http服务器?

+2

为什么总是有人问一个游戏框架相关的问题,也有对1.x和2.x版本的答案?鉴于它们如此不同,这只会导致混淆,如果刚刚命名为不同的名称......就像'Play'和'Run'一样。 –

+0

大家都在玩Play!当他们推出2.0时就组队了。他们没有听。 – HelpMeStackOverflowMyOnlyHope

回答

21

即使用户直接输入http://,也可以使用拦截器重定向每个请求。下面是我使用的代码(它运行在运行无容器play run时,或者在Heroku前端运行时)。

public class HttpsRequired extends Controller { 
    /** Called before every request to ensure that HTTPS is used. */ 
    @Before 
    public static void redirectToHttps() { 
     //if it's not secure, but Heroku has already done the SSL processing then it might actually be secure after all 
     if (!request.secure && request.headers.get("x-forwarded-proto") != null) { 
      request.secure = request.headers.get("x-forwarded-proto").values.contains("https"); 
     } 

     //redirect if it's not secure 
     if (!request.secure) { 
      String url = redirectHostHttps() + request.url; 
      System.out.println("Redirecting to secure: " + url); 
      redirect(url); 
     } 
    } 

    /** Renames the host to be https://, handles both Heroku and local testing. */ 
    @Util 
    public static String redirectHostHttps() { 
     if (Play.id.equals("dev")) { 
      String[] pieces = request.host.split(":"); 
      String httpsPort = (String) Play.configuration.get("https.port"); 
      return "https://" + pieces[0] + ":" + httpsPort; 
     } else { 
      if (request.host.endsWith("domain.com")) { 
       return "https://secure.domain.com"; 
      } else { 
       return "https://" + request.host; 
      } 
     } 
    }  
} 
+0

你太棒了! –

2

你应该能够。执行以下操作:

  1. 设置http.port并在application.config文件https.port
  2. 用户@@ {Controller.action()固定()}当你需要指向一个安全页。同时使用@@生成一个完整的URL(包括HTTPS)和安全暗示播放你想要HTTPS协议

这应该工作

+2

如果用户在地址栏中键入url http://myapp.org/login而不是https://myapp.org/login,该怎么办?我需要的是来自控制器login()本身的https重定向。 – emt14

0

这似乎并不可能从视图控制器点。从模板Pere解决方案工作,但这只能从模板生成https网址。

如果用户通过手动输入或在链接到http url后访问登录操作,似乎没有办法强制/重定向到https。

最好的方法似乎有一个前端代理。

+0

实际上有几种方法可以完成此任务,因为您可以在其他答案中看到 –

3

我认为你可以在控制器中检查request.secure == true,然后重定向到https。

+0

您有Play 1.x记?我在Play 2.2中找不到任何'request.secure'字段。 – KajMagnus

+1

'request.sequre'在2014年6月发布的Play 2.3中提供,请参阅API文档:http://www.playframework.com/documentation/2.3.x/api/scala/index.html#play.api。 mvc.RequestHeader – KajMagnus

6

下面是一个适用于Java Play 2.1.1和Heroku的示例。

public class ForceHttps extends Action<Controller> { 

    // heroku header 
    private static final String SSL_HEADER = "x-forwarded-proto"; 

    @Override 
    public Result call(Context ctx) throws Throwable { 
     final Result result; 
     if (Play.isProd() && !isHttpsRequest(ctx.request())) { 
      result = redirect("https://" + ctx.request().host() 
        + ctx.request().uri()); 
     } 
     else { 
      // let request proceed 
      result = this.delegate.call(ctx); 
     } 
     return result; 
    } 

    private static boolean isHttpsRequest(Request request) { 
     // heroku passes header on 
     return request.getHeader(SSL_HEADER) != null 
       && request.getHeader(SSL_HEADER) 
         .contains("https"); 
    } 

} 

然后,对于任何想要检查https的控制器,添加@With(ForceHttps.class)。或者如果你想要所有的控制器检查,然后添加一个类HttpsController扩展控制器,并让所有类扩展HttpsController。

例如

@With(ForceHttps.class) 
public class HttpsController extends Controller { 

} 
+1

这对2.5我没有任何问题,唯一的区别是调用方法的返回类型现在是CompletionStage ,所以你可以使用CompletableFuture.completedFuture返回重定向。 – jyoung

3

如果您使用AWS,您可以在负载平衡器终止您的HTTPS和使用过滤器重定向HTTP连接到HTTPS。

AWS CONF:

443(负载平衡器)----------> 80(服务器)

80(负载平衡器)---------- > 80(服务器)

过滤器:

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