2015-08-31 6 views
32

Я пытаюсь вызвать конечные точки REST на одно приложение (приложение с пружинной загрузкой) из другого (угловое). Приложения работают на следующих хостах и ​​портах.CORS с пружинным ботинком и angularjs не работает

  • REST приложение, с помощью загрузки пружины, http://localhost:8080
  • HTML приложения, используя angularjs, http://localhost:50029

Я также использую spring-security с применением пружинного загрузки. В приложении HTML я могу выполнить проверку подлинности в приложении REST, но после этого я до сих пор не могу получить доступ к какой-либо конечной точке REST. Например, у меня есть функция angularjs, определенная следующим образом.

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; 
}]); 

Контроллер angularjs выглядит следующим образом.

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!'); 
      }); 
    }; 
}]); 

На призыв к http://localhost:8080/api/admin/isloggedin, я получаю 401 Unauthorized.

На стороне приложения REST у меня есть фильтр CORS, который выглядит следующим образом.

@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 { } 
} 

Конфигурация моей весенней безопасности выглядит следующим образом.

@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); 
    } 
} 

Всех обработчики делают это выписывая ответ JSON, как {success: true} на основе, если пользователь вошел в системе, не удался проверить подлинность, или выхода из системы. RestAuthenticationEntryPoint выглядит следующим образом.

@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"); 
    } 

} 

Любые идеи о том, чего я не хватает или что-то не так?

+0

Я полагаю, вы должны выполнить аутентификацию, а также, как знак или что-то. У вас есть 2 сервера. Вы посмотрели на этот учебник? Https: //spring.io/guides/tutorials/spring-security-and-angular-js/ –

+0

@GokhanOner Как переносить аутентификацию? Вероятно, это недостающая часть этой проблемы. Кроме того, да, я проходил эти учебные пособия и не думал, что они согласуются с моим подходом. Первые две части касались аутентификации Http-Basic, затем третья часть была посвящена Redis (я не хотел или планировал получить это как зависимость), а затем последний учебник касался «API-шлюза» с весенним облаком, который я мысль была излишней. –

+0

Я полагаю, вы можете сделать это без redis, это всего лишь хранилище кеш-значений. Вам необходимо сохранить аутентификацию и токен CSRF в хранилище, возможную внутреннюю карту на лету. Ключевым моментом здесь является ключ аутентификации. посмотрите на пример: https: //github.com/dsyer/spring-security-angular/tree/master/spring-session и страницу с «сервером ресурсов». Вы увидите, что некоторые дополнительные компоненты определены, порядок фильтра CORS также важен. И некоторая поддержка. меняется тоже необходимо. –

ответ

56
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() { 
} 

} 

Не нужно дополнительно определять этот фильтр, просто добавьте этот класс. Весна будет сканировать и добавить ее для вас. SimpleCORSFilter. Вот пример: spring-enable-cors

+0

Несколько вопросов. 1) Где я должен помещать строковые константы 'HEADERS' и' X_REDIRECT_LOCATION_HEADER'? 2) Является ли строка 'request.getRequestURL());' ошибкой опечатки или копирования/вставки? 3) Почему вы не проверяете «ОПЦИИ» и просто продолжаете с цепочкой фильтров? –

+2

Но он блокирует выполнение AuthenticationEntryPoint .. Пожалуйста, руководство – Prateek

+1

Большое спасибо, это очень помогло мне в моей борьбе за то, чтобы весна и ember работали вместе. Привет, друг! –

7

Я был в подобной ситуации. После проведения исследований и испытаний, вот мои выводы:

  1. С весной Ботинками, рекомендуемым способом включить глобальное CORS это объявить в Spring MVC и в сочетании с мелкозернистой @CrossOrigin конфигурации, как:

    @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. Теперь, так как вы используете Spring Security, вы должны включить CORS на уровне Spring Security, а также, чтобы позволить ему использовать конфигурацию, определенную на уровне Spring MVC, как:

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

    Here - очень отличный учебник, объясняющий поддержку CORS в Spring MVC framework.

+0

это не работает для меня :( – Osgux

+0

@Osgux какой ошибки поживаете ?? –

+0

взлетов он работает с этим изменением HTTP .csrf() .disable() .cors() й() – Osgux

0

проверить это:

@Override 
protected void configure(HttpSecurity httpSecurity) throws Exception { 
    ... 
      .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 
    ... 
} 
+0

Хотя этот код может ответить на вопрос, предоставляя дополнительный контекст относительно того, почему и/или как этот код отвечает на вопрос, улучшает его долгосрочную ценность. – rollstuhlfahrer

Смежные вопросы