title: In memory spring security behind a reverse proxy (with HTTPS)
body: <p>My basic setup is as follows:</p>
  
  <p>(public web) &lt;----> (nginx ports 80/443) &lt;----> (tomcat8 port 8080 [using controller classes for /admin and /public])</p>
  
  <p>nginx is operating on two domain names, and acts as a reverse proxy between a single tomcat8 servlet and the public web.</p>
  
  <p>The reason I chose this setup was so that I could have a single servlet which is used by both the public-facing site (<a href="http://example.com:80" rel="nofollow noreferrer">http://example.com:80</a>), and the admin pages (<a href="https://admin.example.com:443" rel="nofollow noreferrer">https://admin.example.com:443</a>).  The proxy forwards 80 to localhost:8080/public, and port 443 is forwarded to localhost:8080/admin.</p>
  
  <p><strong>The Main Problem</strong></p>
  
  <p>Much of the online resources relating to Spring Security are for simpler setups, where you can always rely on the URI as it appears in the servlet to be the same as the URI as it appears on the public web.</p>
  
  <p>Normally a controller doesn't have to be written for POST on /login, but in my case when I omit such a method, in my controller class for /admin, I get the following error: Status "405 - Request method 'POST' not supported".</p>
  
  <p>On the other hand, if I include a controller function for POST on /login in the /admin controller class, traffic for POST from the login form is simply directed to that controller; I have failed so far to find any documentation on what to do with the params for user and password after that point, since normally Spring Security just handles POST automatically, as it would seem from Github repos I examined using Spring Security, and not implementing any @Controllers for login POST data.</p>
  
  <p>Configuration:</p>
  
  <pre><code>import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  
  @Configuration
  @EnableWebSecurity
  public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests()
          .antMatchers("/admin/login", "/public/**").permitAll()
          .anyRequest().authenticated()
          .and().formLogin()
          .loginPage("/login").usernameParameter("email").passwordParameter("pass");
  }
  
  @Autowired
  public void configureGlobal( AuthenticationManagerBuilder auth ) throws Exception {
      auth
          .inMemoryAuthentication()
          .withUser("john.smith@example.com").password("password").roles("USER");
  }
  }
  </code></pre>
  
  <p>Controller:</p>
  
  <pre><code>import org.springframework.stereotype.Controller;
  import org.springframework.ui.ModelMap;
  import org.springframework.web.bind.annotation.*;
  
  import java.io.IOException;
  
  @Controller
  @RequestMapping(value = "/admin")
  public class AdminController extends AbstractController {
      @RequestMapping(value = "")
      public String adminHome(
          ModelMap model
      ) {
          return "success";
      }
      @RequestMapping(value = "login", method = RequestMethod.GET)
      public String adminLogin(
          ModelMap model,
      ) throws IOException {
          getAdminConstants(model);
          return "admin/login";
      }
  //  @RequestMapping(value = "login", method = RequestMethod.POST)
  //  public String adminProcessLogin(
  //      ModelMap model,
  //  ) throws IOException {
  //      return "success";
  //  }
  }
  </code></pre>
  
  <p>web.xml:</p>
  
  <pre><code>[...]
  &lt;filter&gt;
      &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
      &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
  &lt;/filter&gt;
  &lt;filter-mapping&gt;
      &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
      &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
  [...]
  </code></pre>
  
  <p>The parts of the security configuration file in charge of what <em>can</em> be viewed without user login work exactly as expected, for example when I visit example.com (forwarded internally to localhost:8080/public/$uri), I am not prompted for a login.  When I go to an admin page (through <a href="https://admin.example.com" rel="nofollow noreferrer">https://admin.example.com</a>), I am correctly prompted for login.  <em>However</em>, it appears these login details go nowhere after this point (although the HTML form does submit a POST request containing the sought attributes).  I either receive an error about there being no controller for POST, or if I write one then I feel the entire purpose of Spring Security is thrown out the window.</p>
  
  <p>Anyone have experience with a similar setup who sees something I'm missing?</p>
  
  <p><strong>EDIT:</strong> Adding relevant nginx config:</p>
  
  <pre><code>server {
      listen 80;
      listen [::]:80;
      server_name admin.example.com;
      return 301 https://admin.example.com$request_uri;
  }
  server {
      listen 80;
      listen [::]:80;
      server_name example.com
      location / {
          proxy_pass http://127.0.0.1:8080/public/;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
      }
  }
  server {
      listen 443 ssl;
      listen [::]:443 ssl;
      server_name admin.example.com;
      ssl_certificate /etc/letsencrypt/live/admin.example.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/admin.example.com/privkey.pem;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      location / {
          proxy_pass http://127.0.0.1:8080/admin/;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
      }
  }
  </code></pre>