I am trying to call REST endpoints on one application (spring-boot application) from another (angularjs). The applications are running on the following hosts and ports.
- REST application, using spring boot,
http://localhost:8080
- HTML application, using angularjs,
http://localhost:50029
I am also using spring-security
with the spring-boot application. From the HTML application, I can authenticate to the REST application, but, thereafter, I still cannot access any REST endpoint. For example, I have an angularjs service defined as follows.
adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) { var s = {}; s.isAdminLoggedIn = function(data) { return $http({ method: 'GET', url: 'http://localhost:8080/api/admin/isloggedin', withCredentials: true, headers: {'X-Requested-With': 'XMLHttpRequest' } }); }; s.login = function(username, password) { var u = 'username='+ encodeURI(username); var p = 'password='+ encodeURI(password); var r = 'remember_me=1'; var data = u +'&'+ p +'&'+ r; return $http({ method: 'POST', url: 'http://localhost:8080/login', data: data, headers: {'Content-Type': 'application/x-www-form-urlencoded'} }); }; return s;}]);
The angularjs controller looks like the following.
adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) { $scope.username = ''; $scope.password = ''; $scope.signIn = function() { AdminService.login($scope.username, $scope.password) .success(function(d,s) { if(d['success']) { console.log('ok authenticated, call another REST endpoint'); AdminService.isAdminLoggedIn() .success(function(d,s) { console.log('i can access a protected REST endpoint after logging in'); }) .error(function(d, s) { console.log('huh, error checking to see if admin is logged in'); $scope.reset(); }); } else { console.log('bad credentials?'); } }) .error(function(d, s) { console.log('huh, error happened!'); }); };}]);
On the call to http://localhost:8080/api/admin/isloggedin
, I get a 401 Unauthorized
.
On the REST application side, I have a CORS filter that looks like the following.
@Component@Order(Ordered.HIGHEST_PRECEDENCE)public class CORSFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; response.setHeader("Access-Control-Allow-Origin", "http://localhost:50029"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token"); response.setHeader("Access-Control-Allow-Credentials", "true"); if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) { chain.doFilter(req, res); } } @Override public void init(FilterConfig config) throws ServletException { }}
My spring security configuration looks like the following.
@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private JsonAuthSuccessHandler jsonAuthSuccessHandler; @Autowired private JsonAuthFailureHandler jsonAuthFailureHandler; @Autowired private JsonLogoutSuccessHandler jsonLogoutSuccessHandler; @Autowired private AuthenticationProvider authenticationProvider; @Autowired private UserDetailsService userDetailsService; @Autowired private PersistentTokenRepository persistentTokenRepository; @Value("${rememberme.key}") private String rememberMeKey; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint(restAuthenticationEntryPoint) .and() .authorizeRequests() .antMatchers("/api/admin/**").hasRole("ADMIN") .antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .successHandler(jsonAuthSuccessHandler) .failureHandler(jsonAuthFailureHandler) .permitAll() .and() .logout() .deleteCookies("remember-me", "JSESSIONID") .logoutSuccessHandler(jsonLogoutSuccessHandler) .permitAll() .and() .rememberMe() .userDetailsService(userDetailsService) .tokenRepository(persistentTokenRepository) .rememberMeCookieName("REMEMBER_ME") .rememberMeParameter("remember_me") .tokenValiditySeconds(1209600) .useSecureCookie(false) .key(rememberMeKey); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .authenticationProvider(authenticationProvider); }}
All the handlers are doing is writing out a JSON response like {success: true}
based on if the user logged in, failed to authenticate, or logged out. The RestAuthenticationEntryPoint
looks like the following.
@Componentpublic class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex) throws IOException, ServletException { resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); }}
Any ideas on what I am missing or doing wrong?