Enforce Https routing for login with play framewor

2019-01-13 14:18发布

问题:

I want to enforce https routing for the login page only of my application.

Is it possible to do so with Play! without the use of a front end http server?

回答1:

You can use an @Before interceptor to redirect every request, even if the user types http:// directly. Below is the code that I use (it works when running containerless play run, or when running behind a front end such as on 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;
            }
        }
    }    
}


回答2:

Here is an example that works with Java Play 2.1.1 and 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");
    }

}

Then to any controllers you want to check for https, add @With(ForceHttps.class). Or if you want all controllers to check, then add a class HttpsController extends Controller and have all your classes extend HttpsController.

e.g.

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

}


回答3:

I think that you can check in the controller for request.secure == true and then redirect to https.



回答4:

If your using AWS, you can terminate your HTTPS at the Load Balancer and use a filter to redirect HTTP connection to HTTPS.

AWS Conf:

443 (Load Balancer) ----------> 80 (Server)

80 (Load Balancer) ----------> 80 (Server)

The Filter:

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


回答5:

You should be able. Do the following:

  1. Set up http.port and https.port in the application.config file
  2. User @@{Controller.action().secure()} when you need to point to a secure page. Use both @@ to generate a full url (including https) and secure to hint to Play you want HTTPS protocol

This should work



回答6:

It does not seem to be possible from the controller point of view. From the template Pere solution works but this only generates the https url from the template.

If the user accesses the login action by either typing manually or following a link to the http url, there does not seem to be a way to enforce/redirect to https.

The best way seems to have a front end proxy.