Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CORS with spring-boot and angularjs not working

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 @EnableWebSecurity public 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.

@Component public 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?

like image 377
Jane Wayne Avatar asked Aug 31 '15 20:08

Jane Wayne


2 Answers

import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;  @Component public class SimpleCORSFilter implements Filter {  private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);  public SimpleCORSFilter() {     log.info("SimpleCORSFilter init"); }  @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {      HttpServletRequest request = (HttpServletRequest) req;     HttpServletResponse response = (HttpServletResponse) res;      response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));     response.setHeader("Access-Control-Allow-Credentials", "true");     response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");     response.setHeader("Access-Control-Max-Age", "3600");     response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");      chain.doFilter(req, res); }  @Override public void init(FilterConfig filterConfig) { }  @Override public void destroy() { }  } 

No need extra define this filter just add this class. Spring will be scan and add it for you. SimpleCORSFilter. Here is the example: spring-enable-cors

like image 155
abosancic Avatar answered Oct 04 '22 23:10

abosancic


I had been into the similar situation. After doing research and testing, here is my findings:

  1. With Spring Boot, the recommended way to enable global CORS is to declare within Spring MVC and combined with fine-grained @CrossOrigin configuration as:

    @Configuration public class CorsConfig {      @Bean     public WebMvcConfigurer corsConfigurer() {         return new WebMvcConfigurerAdapter() {             @Override             public void addCorsMappings(CorsRegistry registry) {                 registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")                         .allowedHeaders("*");             }         };     } } 
  2. Now, since you are using Spring Security, you have to enable CORS at Spring Security level as well to allow it to leverage the configuration defined at Spring MVC level as:

    @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {      @Override     protected void configure(HttpSecurity http) throws Exception {         http.cors().and()...     } } 

    Here is very excellent tutorial explaining CORS support in Spring MVC framework.

like image 38
Yogen Rai Avatar answered Oct 04 '22 23:10

Yogen Rai