title: Spring Boot and Security with custom AngularJS Login page
body: <p>I'm implementing a custom AngularJS login page for Spring Security and having issues authenticating.</p>
  
  <p>Followed this tutorial/example, and their <a href="https://github.com/dsyer/spring-security-angular/tree/master/single" rel="noreferrer">example works fine locally</a>.</p>
  
  <p>However, when I try to implement this myself, authentication fails. I'm not sure where my mistake is.</p>
  
  <p><strong>A POST is made to /login with credentials, (the curl is identical to the example), and I receive a 302 Found with a redirect to GET /login/, which returns a 404 Not Found.</strong></p>
  
  <p>When I try to POST to /login, Spring does not generate any debug logs. So I'm not sure how it is serving the 302.</p>
  
  <p>My code can be found <a href="https://github.com/AndrewBell/spring-angular-starter/tree/master" rel="noreferrer">here</a>:</p>
  
  <p>Notable changes (And most likely the source of my issues):</p>
  
  <ul>
  <li><p>File structure changes</p></li>
  <li><p>Using strictly Angular (No jQuery) - Which results in a different function needed to make the POST request</p></li>
  <li><p>Using bower instead of wro4j</p></li>
  <li><p>Angular code styling/scoping</p></li>
  </ul>
  
  <p>Many related Spring Security questions suggest the POST request is formatted incorrectly, but mine appears to be the same as the example (at least when I copy to curl in chrome dev console). Others suggest implementing custom authorization providers, but it is not needed in the example, so I'm perplexed to what the difference is between mine and the example. Help me Stack Exchange, you're my only hope.</p>
  
  <p>Dev Tools: imgurDOTcom/a/B2KmV</p>
  
  <p>Relevant code:</p>
  
  <p><strong>login.js</strong></p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>'use strict';
  angular
      .module('webApp')
      .controller('LoginCtrl', ['$root`enter code here`Scope', '$scope', '$http', '$location', '$route', function($rootScope, $scope, $http, $location, $route) {
          console.log("LoginCtrl created.");
  
          var vm = this;
          vm.credentials = {
              username: "",
              password: ""
          };
          //vm.login = login;
  
          $scope.tab = function(route) {
              return $route.current &amp;&amp; route === $route.current.controller;
          };
  
          var authenticate = function(callback) {
  
              $http.get('user').success(function(data) {
                  console.log("/user success: " + JSON.stringify(data));
                  if (data.name) {
                      console.log("And Authenticated!");
                      $rootScope.authenticated = true;
                  } else {
                      console.log("But received invalid data.");
                      $rootScope.authenticated = false;
                  }
                  callback &amp;&amp; callback();
              }).error(function(response) {
                  console.log("/user failure." + JSON.stringify(response));
                  $rootScope.authenticated = false;
                  callback &amp;&amp; callback();
              });
  
          };
  
          authenticate();
  
          $scope.login = function() {
  
              var data2 = 'username=' + encodeURIComponent(vm.credentials.username) +
                  '&amp;password=' + encodeURIComponent(vm.credentials.password);
  
              $http.post('login', data2, {
                  headers : {
                      'Content-Type': 'application/x-www-form-urlencoded'
                  }
              }).success(function() {
                  authenticate(function() {
                      if ($rootScope.authenticated) {
                          console.log("Login succeeded");
                          $location.path("/");
                          $scope.error = false;
                          $rootScope.authenticated = true;
                      } else {
                          console.log("Login failed with redirect");
                          $location.path("/login");
                          $scope.error = true;
                          $rootScope.authenticated = false;
                      }
                  });
              }).error(function() {
                  console.log("Login failed");
                  $location.path("/login");
                  $scope.error = true;
                  $rootScope.authenticated = false;
              })
          };
  
          $scope.logout = function() {
              $http.post('logout', {}).success(function() {
                  $rootScope.authenticated = false;
                  $location.path("/");
              }).error(function() {
                  console.log("Logout failed");
                  $rootScope.authenticated = false;
              });
          }
  
      }]);</code></pre>
  </div>
  </div>
  </p>
  
  <p><strong>application.java</strong></p>
  
  <pre class="lang-java prettyprint-override"><code>package com.recursivechaos.springangularstarter;
  
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  import org.springframework.boot.autoconfigure.security.SecurityProperties;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.core.annotation.Order;
  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  import org.springframework.security.web.csrf.CsrfFilter;
  import org.springframework.security.web.csrf.CsrfToken;
  import org.springframework.security.web.csrf.CsrfTokenRepository;
  import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RestController;
  import org.springframework.web.filter.OncePerRequestFilter;
  import org.springframework.web.util.WebUtils;
  
  import javax.servlet.Filter;
  import javax.servlet.FilterChain;
  import javax.servlet.ServletException;
  import javax.servlet.http.Cookie;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.io.IOException;
  import java.security.Principal;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.UUID;
  
  @SpringBootApplication
  @RestController
  public class Application {
  
      public static void main(String[] args) {
          SpringApplication.run(Application.class, args);
      }
  
      @RequestMapping("/user")
      public Principal user(Principal user) {
          return user;
      }
  
      @RequestMapping("/resource")
      public Map&lt;String, Object&gt; home() {
          Map&lt;String, Object&gt; model = new HashMap&lt;&gt;();
          model.put("id", UUID.randomUUID().toString());
          model.put("content", "Hello World");
          return model;
      }
  
      @Configuration
      @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
      protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.
                  formLogin().
                  //loginPage("/#/login").
              and().
                  logout().
              and().
                  authorizeRequests().
                  antMatchers("/index.html", "/home/**", "/login/**", "/bower_components/**", "/", "/main.js", "/login/", "/navigation/**","/login","login/","/login.html").
                  permitAll().
                  anyRequest().
                  authenticated().
              and().
                  csrf().
                  csrfTokenRepository(csrfTokenRepository()).
              and().
                  addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
          }
  
          private Filter csrfHeaderFilter() {
              return new OncePerRequestFilter() {
                  @Override
                  protected void doFilterInternal(HttpServletRequest request,
                                                  HttpServletResponse response, FilterChain filterChain)
                      throws ServletException, IOException {
                      CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
                          .getName());
                      if (csrf != null) {
                          Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
                          String token = csrf.getToken();
                          if (cookie == null || token != null
                              &amp;&amp; !token.equals(cookie.getValue())) {
                              cookie = new Cookie("XSRF-TOKEN", token);
                              cookie.setPath("/");
                              response.addCookie(cookie);
                          }
                      }
                      filterChain.doFilter(request, response);
                  }
              };
          }
  
          private CsrfTokenRepository csrfTokenRepository() {
              HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
              repository.setHeaderName("X-XSRF-TOKEN");
              return repository;
          }
      }
  
  }
  </code></pre>