2

I'm having loads of trouble getting my Java web app working on Heroku.

This is what I have:

A Java web app (standard war file) using Spring Security with a security-constraint section in my web.xml that looks like this:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>SSL URLs</web-resource-name>
            <url-pattern>/j_spring_security_check</url-pattern>
            <url-pattern>/secure/account/create</url-pattern>
            <url-pattern>/register</url-pattern>
            <url-pattern>/login/*</url-pattern>
            <url-pattern>/</url-pattern> 
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint> 

When I deploy my WAR file to Heroku (using the Heroku deploy plugin for Atlassian Bamboo) and the app starts up, I end up getting a 'too many redirects' error in my browser - it looks like it has something to do with flicking between https and http but I can't figure out what I need to do to fix it.

I just want to use the piggyback SSL for now, as the SSL add-on is quite pricey for my hobby project (at $20 a month).

JMM
  • 3,922
  • 6
  • 39
  • 46
  • This is probably due to how Heroku always talks http (not https) to the app. I have an example app that handles this in a different way: https://github.com/jamesward/springmvc-https-enforcer Not sure if that helps though. – James Ward Nov 15 '12 at 18:22
  • Hmm, so it's either all or nothing with the SSL then? – JMM Nov 15 '12 at 20:36
  • No. It doesn't have to be. But you might have to do some extra work on Heroku to correctly determine if the request was originally https or not. You will have to use the `x-forwarded-proto` request header. This might help: http://stackoverflow.com/questions/5741210/handling-x-forwarded-proto-in-java-apache-tomcat – James Ward Nov 15 '12 at 21:14

3 Answers3

12

I solved a similar problem by using a combination of requires-channel in my Spring Security configuration and Tomcat's RemoteIpValve. If you are not using Spring Security, configuring RemoteIpValve should be sufficient (the following assumes you are using the excellent webapp-runner as your container) I imagine there is an equivalent for jetty-runner, but I don't know it...

The issue is that Heroku's web/routing tier handles the SSL, and proxies the request to your webapp as plain HTTP. So Tomcat doesn't know the request is secured elsewhere (SSL offloading) RemoteIpValve essentially over-rides the protocol, port and IP address that Tomcat sees (and, in turn, how request.isSecure() is evaluated) by looking at HTTP headers set by the proxying server that handled the SSL.

Create a context.xml with the following:

<Context>
  <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="x-forwarded-for" protocolHeader="x-forwarded-proto" portHeader="x-forwarded-port"/>
</Context>

Those three headers (x-forwarded-for, x-forwarded-proto, and x-forwarded-port) are automatically sent by Heroku's routing tier.

Next, ensure that the file gets copied somewhere in your maven target dir (I put it in the same dir as webapp-runner itself). In pom.xml:

<resources>
  <resource>
    <directory>${basedir}/src/main/resources/META-INF</directory>
    <includes>
      <include>context.xml</include>
    </includes>
    <targetPath>${project.build.directory}/dependency</targetPath>
  </resource>
</resources>

Finally, make sure webapp-runner points to the context.xml (e.g., using webapp-runner 7.0.30.1, which supports the context-xml argument). In your Procfile:

web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --context-xml target/dependency/context.xml ...etc...

As a final note, I use of Spring Security requires-channel on <sec:intercept-url> to toggle back and forth between HTTP and HTTPS by flipping an environment variable - unset on my local dev box for easy configuration, set on Heroku (via heroku config) to selectively force SSL in different environments.

Hope this works for you...

bimsapi
  • 4,985
  • 2
  • 19
  • 27
  • Thanks, you are a lifesaver I would have spend hours figuring this out. The only problem I had was the following: I had to add back in the default resources folder in my mvn project. The new resource directive was overriding the default resource folder I was used to automatically working. Thx! src/main/resources\ – Jimmy Johnson Jun 06 '13 at 06:19
  • 1
    Sounds good. Did you perhaps also get a "NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined" on startup? I get it as soon as I add the in the pom.xml. I get it even before specifying the --context-xml flag. Any ideas? – Markus Coetzee Jun 14 '13 at 17:30
  • Life saving. Thanks a lot. – Junjie May 08 '17 at 13:22
3

I was facing the exact same problem using webapp-runner and fixed it by simply using the --proxy-base-url https://example.com, as described here

Community
  • 1
  • 1
Amaranth
  • 78
  • 3
  • this works for me. it's simple and also I can use http in localhost and https on heroku without any extra change. – Vasile Bors Jun 14 '18 at 18:23
  • Hello, I had the same issue and added this line to my Procfile: --proxy-base-url https:// myappname.herokuapp.com , but now I get an application error (time out). Is there something I did wrong or does it mean that your answer does not apply to my case? Thank you! – Guillaume May 09 '19 at 10:40
1

The solution provided by @bimsapi worked for me. I ran into two issues using this solution. Once I solved those it worked perfectly.

First, I had to put the context.xml file in META-INF directory directly under the root in the war file.

Second, the path to the context.xml file you provide to --context-xml attribute is the relative path from the root directory on the file system not the war file. Such as --context.xml ./tartget/myapp/META-INF/context.xml.

Before I was giving --context.xml META-INF/context.xml which was wrong.

Gautam Savaliya
  • 1,403
  • 2
  • 20
  • 31
Muneer Y
  • 83
  • 1
  • 8