I am trying to implement a custom login page with Spring Boot and AngularJS that uses a REST call to authenticate. When I try to login, the POST method is being rejected with a 405 Method not allowed response. Here's what I have as my setup. I have set the loginProcessingUrl and set permitAll on the login form. Other than that, I've basically followed the example from this article. Here's my setup:
SecruityConfiguration.java
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
Environment env;
@Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/webjars/**", "/modules/**", "/*.js")
.permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin()
.permitAll()
.loginPage("/login")
.loginProcessingUrl("/api/authentication/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and()
.logout()
.logoutUrl("/api/authentication/logout")
.logoutSuccessUrl("/login")
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
//Other code below...
Authentication Entry Point
@Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)
throws IOException, ServletException {
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Failure Handler
@Component
public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)
throws IOException, ServletException {
super.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
}
}
Success Handler
@Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
clearAuthenticationAttributes(request);
}
}
I cannot see what I'm missing with this setup, it seems it should work. If any other code is needed I'm happy to provide. Thanks for any help!
Edit: Adding Active Directory LDAP code for authenticating. Code exists in SecurityConfiguration.java
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider ldapAuthenticationProvider =
new ActiveDirectoryLdapAuthenticationProvider(env.getProperty("ldap.domain"), env.getProperty("ldap.url"));
ldapAuthenticationProvider.setConvertSubErrorCodesToExceptions(true);
ldapAuthenticationProvider.setUseAuthenticationRequestCredentials(true);
ldapAuthenticationProvider.setUserDetailsContextMapper(userDetailsContextMapper());
auth.authenticationProvider(ldapAuthenticationProvider);
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new LDAPDetailsContextMapper();
}
The login page sends a credentials object like so: {"username":"foo","password":"bar"}
Edit: Adding Angular code for login
Angular Service
function login(credentials) {
var deferred = $q.defer();
$http.post('/api/authentication/login', credentials)
.success(loginComplete)
.error(loginFailed);
// Promise for successful response. Return data to the controller
function loginComplete(data) {
user = data;
deferred.resolve(data);
}
// Promise for failed response. Logs error to console.
function loginFailed(err) {
deferred.reject(err, credentials);
}
// returns the promise
return deferred.promise;
Angular Controller
function login() {
var credentials = {username: vm.username, password: vm.password};
if (validateRequiredFields()) {
authenticationService.login(credentials)
.then(loginSuccess)
.catch(loginFailed);
}
function loginSuccess(user) {
$rootScope.user = user;
//change route here
var redirectUrl = '';
if ($location.search().redirect) {
redirectUrl = $location.search().redirect;
} else {
redirectUrl = '/';
}
$location.url(redirectUrl);
}