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“>
Antworten
Du musst angemeldet sein, um einen Kommentar abzugeben.