Skip to content

Latest commit

 

History

History
374 lines (281 loc) · 14 KB

README.textile

File metadata and controls

374 lines (281 loc) · 14 KB

ReverseProxy module for Play!

What is ReverseProxy?

ReverseProxy allows developers to configure web applications to
automatically switch between the HTTP and HTTPS protocols per page
when used behind a front end Reverse-Proxy server such as Apache, Nginx or Lighttpd.
Configuration is done in Controllers using annotations.

Requirements

  • A Reverse-Proxy server configured
  • For SSL, a certificate and its key
  • A Play! app

Configuration

Set the following properties in conf/application.conf

bc.
reverse_proxy.http.address=127.0.0.1
reverse_proxy.http.port=80
reverse_proxy.https.port=443

  • reverse_proxy.enable – For enabling or disabling the switching between the Play-App and the Reverse-Proxy.
  • reverse_proxy.http.address – The Reverse-Proxy address.
  • reverse_proxy.http.port – The Reverse-Proxy HTTP listening port.
  • reverse_proxy.https.port – The Reverse-Proxy HTTPS listening port.

HTTPS support

In order to communicate between Reverse-Proxy server and Play built-in server either by HTTP or HTTPS protocol,
Play needs Java keystore or simple cert and key files and since you have to create a Certificate and its Key for the Reverse-Proxy server,
you can use the same ones
.

To start an HTTPS connector for your application, just declare the https.port configuration property in your application.conf file:

http.port=9000
https.port=9443
  • http.port – The built-in server HTTP port, it must be configured explicitly
  • https.port – The built-in server HTTPS port, it must be configured explicitly

Disabling ReverseProxy module

If you wish or have to disable this module by any reason without removing it from the dependencies, just set the following property in you application.conf file:

reverse_proxy.enable=false

In this case, if you have configured http.port and https.port, the process for switching process between HTTP and HTTPS schemes will use those configured ports.
If you only have configured http.port, this will be the only port used, and won’t be any switching process between HTTP and HTTPS schemes.

Configuration Exception

If the above properties configuration are not correct, the module wil throw an configure exception and will stop the application.

Certificates

You need to put your certificates in the conf directory. Play supports X509 certificates and keystore certificates. The X509 certificates must be named as follow:
host.cert for the certificate and host.key for the key. If you are using keystore, then, by default it should be named certificate.jks.

If you are using X509 certificates, then the following parameters can be configured through your application.conf:

# X509 certificates
certificate.key.file=conf/host.key
certificate.file=conf/host.cert
# In case your key file is password protected
certificate.password=secret
trustmanager.algorithm=JKS

In case your are using keystore:

keystore.algorithm=JKS
keystore.password=secret
keystore.file=conf/certificate.jks

Note that the values above are the default ones. If you use these default values, they are not required to be explicitly in application.conf file,
however, if your certificate doesn’t use the default names, you must add your custom values.

certificate.key.file=conf/localhost.key
certificate.file=conf/localhost.cert

You can generate self signed certificates using openssl:

  1. Generate a Private Key
    openssl genrsa -des3 -out host.key 1024
  2. Generate a CSR (Certificate Signing Request)
    openssl req -new -key host.key -out host.csr
  3. Remove Passphrase from Key
    cp host.key host.key.org
    openssl rsa -in host.key.org -out host.key
  4. Generating a Self-Signed Certificate
    openssl x509 -req -days 365 -in host.csr -signkey host.key -out host.crt
  5. Installing the Private Key and Certificate
    Depends on the Reverse-Proxy server.
  6. Configuring SSL Enabled Virtual Hosts
    Depends on the Reverse-Proxy server.

If you are using the java keystore mechanism, then the following properties can be configured in your application.conf:

# Keystore
ssl.KeyManagerFactory.algorithm=SunX509
trustmanager.algorithm=JKS
keystore.password=secret
keystore.file=certificate.jks

The values above are the default values.

You can generate self signed certificates using Java Keytool:

keytool -genkey -keyalg RSA -alias selfsigned -keystore certificate.jks -storepass secret -validity 360 -keysize 1024

Self Signed Certificates Warning

In case you don’t know, when you use a Self Signed Certificate, the Web Browsers always warns about it. If you plan to use your app
in a Production Environment, then you should buy a certificate from a Certificate Authority like VeriSign, Thawte, Geotrust, GoDaddy, Comodo.

Usage

Add this module to your dependecy.yml file

Controllers

There are two annotation for forcing the communication with a Reverse-Proxy server:

  • @SwitchScheme is strictly for Actions and has two optional values:
    • type = SchemeType.HTTP (default value) or SchemeType.HTTPS, for using either HTTP or HTTPS protocol.
    • keepUrl = true or false (default value), for storing the ReferredUrl into a cookie.
    • 
              public class ControllerA extends Controller {
                  @SwitchScheme
                  public static void actionTwo() {
                      // This action will use HTTP (default type)
                  }
                  @SwitchScheme(type = SchemeType.HTTP)
                  public static void actionTwo() {
                      // This action will use HTTP (explicit type)
                  }
                  @SwitchScheme(type = SchemeType.HTTPS)
                  public static void actionThree() {
                      // This action will use HTTPS (explicit type)
                  }
              }
          
  • @GlobalSwitchScheme is strictly for Controllers and has one optional value:
    • type = SchemeType.HTTP (default value) or SchemeType.HTTPS, for using either HTTP or HTTPS protocol.
    • 
              @GlobalSwitchScheme
              public class ControllerA extends Controller {
                  public static void actionOne() {
                      // This action will use HTTP (global default type)
                  }
                  @SwitchScheme
                  public static void actionTwo() {
                      // This action will use HTTP (default type)
                  }
                  @SwitchScheme(type = SchemeType.HTTP)
                  public static void actionThree() {
                      // This action will use HTTP (explicit type)
                  }
                  @SwitchScheme(type = SchemeType.HTTPS)
                  public static void actionFour() {
                      // This action will use HTTPS (explicit type)
                  }
                  // Any other action from this controller will use HTTP (global default type)
              }
              ---
              @GlobalSwitchScheme(type = SchemeType.HTTP)
              public class ControllerB extends Controller {
                  public static void actionOne() {
                      // This action will use HTTP (global explicit type)
                  }
                  @SwitchScheme
                  public static void actionTwo() {
                      // This action will use HTTP (default type)
                  }
                  @SwitchScheme(type = SchemeType.HTTP)
                  public static void actionThree() {
                      // This action will use HTTP (explicit type)
                  }
                  @SwitchScheme(type = SchemeType.HTTPS)
                  public static void actionFour() {
                      // This action will use HTTPS (explicit type)
                  }
                  // Any other action from this controller will use HTTP (explicit type)
              }
              ---
              @GlobalSwitchScheme(type = SchemeType.HTTPS)
              public class ControllerB extends Controller {
                  // All actions from this controller will use HTTPS (explicit global type)
              }
          

With GlobalSwitchScheme all Methods from a Controller will use the specified or default SchemeType value, unless
some of its methods are annotated with SwitchScheme, in which case, those method will override the global SchemeType value.

You shall annotate a method that requires authentication/authorization as:

@SwitchScheme(type = SchemeType.HTTP, keepUrl = true)
public satic void restrictedMethod(){
    ...
}

In this way, a cookie is created for storing the REFERRED_URL and your app shall redirect to a authentication view (e.g., login);
when the app validates a user is authentic, the app shall redirect to the restrictedMethod route (URI Pattern).

@Interceptor

If you are going to use interceptors it is suggested to decouple the controller part from the interceptor part
and annotate the Interceptor Class with @Interceptor, so that when the plugin enhances the controllers, skips the interceptors

See SecureController and SecureInterceptor for clarification, both of them included in this module.

Invoking Actions

An Action from another Action may be INVOKED in one of the following ways:

  • By regular Java call, i.e., Controller.action()
  • By reverse routing, i.e., UrlUtility.redirectByReverseRouting(“Controller.action”) → Invoke GET /action Controller.action
  • By render, i.e., render(“@action”) → renders views/Controller/action.html
public class ControllerA extends Controller {
    public static void actionOne() {
        ControllerA.actionTwo();
    }
    public static void actionTwo(){
        // Do logic...
        // Invoked from actionOne
        render()
    }
}
---
public class ControllerA extends Controller {
    public static void actionOne() {
        ControllerA.actionTwo();
    }
    -
    // Let's supposed this action uses HTTPS
    public static void actionTwo(){
        // Do logic...
        // Invoked from actionOne
        render()
    }
}

Remember an Action must be public static void

Invoking an Action from another Action of the same Controller by Render

For invoking an Action from another Action of the same Controller, you must call one of the following methods:

  • action()
  • Controller.action()
  • package.Controller.action()
  • render(“@action”)
  • render(“@Controller.action”)
  • render(“@package.Controller.action”)
public class ControllerA extends Controller {
    public static void actionOne() {
        render("@actionTwo"); // renders views/Controller/actiontwo.html
    }
    public static void actionTwo(){
        // Do logic...
        // Invoked from actionOne
        render()
    }
}

Invoking an Action from another Action of different Controller by Reverse Routing

For invoking an Action from another Action of different Controller, you may use its Java Call from conf/routes file

GET     /actionTwo                            ControllerB.actionTwo
public class ControllerA extends Controller {
    public static void actionOne() {
        UrlUtility.redirectByReverseRouting("ControllerB.actionTwo"); // Invoke GET /action
    }
}
public class ControllerB extends Controller {
    public static void actionTwo(){
        // Do logic...
        // Invoked from ControllerA.actionOne
        render()
    }
}

Note UrlUtility.redirectByReverseRouting method expects a Java Call as argument, which calculates the Reverse Routing,
and does the redirection according to the URI Pattern (in this example: /action).

Invoking an Action from another Action of different Controller by Java Call

For invoking an Action from another Action of different Controller, you may invoke it directly as a regular Java Call

public class ControllerA extends Controller {
    public static void actionOne() {
        ControllerB.actionTwo();
    }
}
public class ControllerB extends Controller {
    public static void actionTwo(){
        // Do logic...
        // Invoked from ControllerA.actionOne
        render()
    }
}

Secure

Do NOT use the Secure module bundled with Play, instead use SecureController (and its inner class Security) and SecureInterceptor included with this module and implement your own
SecureController.Security for Authentication/Authorization.

See demo-with-secure app for clarification.

Protect

For a better Authentication/Authorization mechanism, use ProtectController (and its inner class Safety) included with this module, which integrates with
Deadbolt module, and implement your own ProtectController.Safety.

See demo-with-deadbolt app for clarification.

Prohibited

There’s a ProhibitedController controller, included with this module, intended to render a forbidden view when a user doesn’t have permission to access a certain area.

See demo-with-deadbolt app for clarification.

Reverse-Proxy Server: static content

If you configured a Reverse-Proxy server for serving static content, then all content withing public folder from other modules must be copied to public
folder of your main application.

Tests

This module has been tested with Play built-in server as standalone in conjunction with Nginx and Apache.

JEE App Container

It has been tested, partially, with Jetty-1.6.26…

Sample application

Two sample demos are part of the distribution.
Don’t forget to run play deps so that it resolves dependencies.

There’s also a example for configuring Nginx as Reverse-Proxy server.

Deprecated classes

The following classes have been annotated as @Deprecated

  • play.modules.reverseproxy.ReverseProxyEnhancer
  • controllers.reverseproxy.ReverseProxyInterceptor

They are not used any more, but they remain in the source code as reference/example of Enhancer and Interceptor, respectively.

Play-Utilities

This module uses a small API: play-utilities

TODO

  • A script for overwrite the layout and css of ReverseProxy.secure.Secure
  • A script for overwrite the layout and css of ReverseProxy.deadbolt.Protect
  • A script for copying all the static content from other modules into the main app

Testing Contributions

If you test this module with another Reverse-Proxy server (e.g., Lighttp) or with another JEE Container, I would really appreciate if publish the results.
Thanks ahead of time.

Credits

Author: Omar O. Román
Coauthor: Omar García G.