title: Spring Security login page redirect with React Router in SPA
body: <p>I’m having a configuration problem with Spring Security re-directing to a login page on my React front end (running with React Router) when running in development mode. I believe the redirect isn't being picked up by my front end, and is due to my noob understanding of how server side routing interacts with SPA routing.</p>
  
  <p>My development front end web server (using Facebook Create React App) is running on localhost:3000, my backend Spring Boot Tomcat on localhost:8080. I’ve enabled Spring Security, which works fine on the Tomcat server (port 8080); accessing localhost:8080 forwards to localhost:8080/login. When accessing localhost:3000, I got a cross-origin error, so added CORS support in Spring as described <a href="https://stackoverflow.com/a/31748398">here</a>. I also consulted <a href="https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#filter-based-cors-support" rel="nofollow noreferrer">this</a> Spring blog post. It seems to work, in that I no longer get the cross-origin error. However, while not logged in, the request from localhost:3000 to localhost:8080/login returns the following:</p>
  
  <pre><code>HTTP/1.1 200 OK
  X-Powered-By: Express
  Accept-Ranges: bytes
  Access-Control-Allow-Origin: *
  Content-Type: text/html; charset=UTF-8
  Content-Length: 1496
  ETag: W/"5d8-80rXYG+kNfQ/xEJ7J2f1PA"
  Vary: Accept-Encoding
  Date: Tue, 10 Jan 2017 12:04:07 GMT
  Connection: keep-alive
  
  &lt;!DOCTYPE html&gt;
  ...
  </code></pre>
  
  <p>This is the index.html hosted in my front end webserver. This is good, but I need it to forward to localhost:3000/login, which will render the login page into index.html.</p>
  
  <p>I believe it's not forwarding to /login because of the routing setup, it goes from <code>localhost:3000/</code> -> <code>HttpSecurity.loginPage("/login")</code> (Spring) -> <code>registry.addViewController("/login").setViewName("forward:/index.html")</code> (Spring) -> <code>&lt;Route path="/" component={ForwardToLandingPage} /&gt;</code> (React Router)</p>
  
  <p>It goes wrong at the end. I want the Tomcat server to re-direct the front end to :3000/login, but instead the front end goes to the  component. Seems there's a problem between the server side redirect and the front end React Router going to the right place. If this is too complex to set up, I might just drop this whole Spring Security in my development environment.</p>
  
  <p>Here are my configurations:</p>
  
  <p><strong>React routing config</strong></p>
  
  <pre><code>ReactDOM.render((
      &lt;Router history={browserHistory}&gt;
          &lt;Route path="/" component={ForwardToLandingPage} /&gt;
          &lt;Route path="/login" component={Login} /&gt;
      &lt;/Router&gt;
      ),
      document.getElementById('root')
  );
  </code></pre>
  
  <p><strong>Spring MVC config</strong></p>
  
  <pre><code>@Configuration
  public class MvcConfig extends WebMvcConfigurerAdapter {
  
      @Override
      public void addViewControllers(ViewControllerRegistry registry) {          
          // This forwarding is required, as my React app is rendering on a div
          // element with the ID 'root' in /index.html. But is this part of the
          // problem?
          registry.addViewController("/").setViewName("forward:/index.html");
          registry.addViewController("/login").setViewName("forward:/index.html");
      }
  }
  </code></pre>
  
  <p><strong>Spring CORS config</strong></p>
  
  <pre><code>@Configuration
  public class MyConfiguration {
  
      @Bean
      public FilterRegistrationBean corsFilter() {
          UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
          CorsConfiguration config = new CorsConfiguration();
          config.setAllowCredentials(true);
          config.addAllowedOrigin("http://localhost:3000");
          config.addAllowedHeader("*");
          config.addAllowedMethod("*");
          source.registerCorsConfiguration("/**", config);
          FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
          bean.setOrder(0);
          return bean;
      }
  }
  </code></pre>
  
  <p><strong>Spring Security config</strong></p>
  
  <pre><code>@EnableWebSecurity
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http
                  .csrf().disable() // temporary fix
                  .authorizeRequests()
                      .antMatchers("/h2-console", "/public/**", "/static/**").permitAll()
                      .anyRequest().authenticated()
                      .and()
                  .formLogin()
                      .loginPage("/login")
                      .permitAll()
                      .and()
                  .logout()
                      .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
      }
  }
  </code></pre>
  
  <p>Any ideas what's going wrong?</p>
  
  <p>I was wondering if the solution to the problem should involve Tomcat, so checked <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html#howto-customize-tomcat-behind-a-proxy-server" rel="nofollow noreferrer">this</a> too.</p>
  
  <p>Thanks for any help! :)</p>