JSF behind reverse proxy
The problem “try to run a jsf application behind a reverse proxy” it will not work if you change the context-path.
Imagine a local deployed jsf application “hello-world.war”. You can start browsing at http://localhost:8080/hello-word/. The browser handles the absolute paths for css and javascript-libs correctly and all forms will work. Remeber that all resource-urls converted to a “contex-absolut-url” by the jsf-framework.
If you open http://localhost:8080/hello-world/index.jsf the the client html will contains resources like this:
<link rel=’stylesheet’ type=‘text/css’
href=’/hello-world/a4j_3_1_3.GAcss/tabPanel.xcss/DATB/eAGTWzQ.BgAD.AG8.jsf’>
<script type=‘text/javascript’ src=‘/hello-world/a4j_3_1_3.GAorg.ajax4jsf.javascript.PrototypeScript.jsf’>
<form id=“j_id14″ name=“j_id14″ method=“post” action=“/hello-world/index.jsf”>
(Note: I use richfaces 3.1.4 in the example)
The browser will append each absolute url (starting with ‘/’) to the host to make the request. And here is the problem if you want to use this jsf-application behind a reverse proxy. Then all absolute URLs pointing to a wrong resource and nothing will work. To try this you can install a local apache web server the following to the httpd.conf:
Listen 9090
ProxyRequests On
ProxyPreserveHost On
ProxyVia full
ProxyPass /external-path/hello-world http://localhost:8080/hello-world/
(This will start the apache at localhost:9090 to avoid port-conflicts)
If you open http://localhost:9090/external-path/hello-world/index.jsf the the client html contains the same absolute urls and the browser try to load these resource from the “wrong server”: http://localhost:9090/hello-world/. If you imagine a real world szenario then the reverse proxy will use a “real world domain” and the urls looks like this http://www.mycompany.de/external-path/hello-world/.
So whats the solution for this? I try my own ViewHandler to convert absolute to relative urls. Two things must be done:
1. Implement the ViewHandler
2. Register the ViewHandler
First the code … we extend javax.faces.application.ViewHandler and overwrite the two methods getActionURL and getResourceURL.
package de.ahoehma.jsf; public class ReverseProxyViewHandler extends ViewHandler { @Override public String getActionURL(final FacesContext context, final String viewId) { return getRelativeURL(context, this.defaultHandler.getActionURL(context, viewId)); } @Override public String getResourceURL(final FacesContext context, final String path) { return getRelativeURL(context, this.defaultHandler.getResourceURL(context, path)); } /** * Transform the given URL to a relative URL <b>in the context of the current * faces request</b>. If the given URL is not absolute do nothing and return * the given url. The returned relative URL is "equal" to the original url but * will not start with a '/'. So the browser can request the "same" resource * but in a relative way and this is important behind reverse proxies! * * @param context * @param theURL * @return */ private String getRelativeURL(final FacesContext context, final String theURL) { final HttpServletRequest request = ((HttpServletRequest) context.getExternalContext().getRequest()); String result = theURL; if (theURL.startsWith("/")) { int subpath = StringUtils.countMatches(getPath(request), "/") - 1; String pathPrefix = ""; if (subpath > 0) { while (subpath > 0) { pathPrefix += "/.."; subpath--; } pathPrefix = StringUtils.removeStart(pathPrefix, "/"); } result = pathPrefix + result; } return result; } /** * Get the url-path from the given request. * * @param request * @return clean path */ private String getPath(final HttpServletRequest request) { try { // TODO handle more than two '/' return StringUtils.replace(new URI(request.getRequestURI()).getPath(), "//", "/"); } catch (final URISyntaxException e) { // XXX URISyntaxException ignored return StringUtils.EMPTY; } } }
The absolute2relative-algorithm prepends for each “/” in the current request-url a “../” to the resource/action-url … thats all
And last register the view-handler. If you use A4J you have to add this to your web.xml:
<context-param>
<param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
<param-value>de.ahoehma.jsf.ReverseProxyViewHandler</param-value>
</context-param>
That’s all
Now if you open http://localhost:9090/external-path/hello-world/index.jsf the client html contains only relative urls:
<link rel=’stylesheet’ type=‘text/css’
href=’../hello-world/a4j_3_1_3.GAcss/tabPanel.xcss/DATB/eAGTWzQ.BgAD.AG8.jsf’>
<script type=‘text/javascript’ src=‘../hello-world/a4j_3_1_3.GAorg.ajax4jsf.javascript.PrototypeScript.jsf’>
<form id=“j_id14″ name=“j_id14″ method=“post” action=“../hello-world/index.jsf”>
Abgelegt unter : Computer, JSF | Getaggt: JSF, Richfaces, Reverse Proxy, Apache, View Handler, a4j