CORS с spring -boot и angularjs не работают

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

  • Приложение REST, используя spring boot, http://localhost:8080
  • приложение HTML, используя angularjs, http://localhost:50029

Я также использую spring-security с приложением spring -boot. В приложении 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 я получаю a 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 { }
}

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

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

}

Любые идеи о том, чего я не вижу или делаю неправильно?

Ответ 1

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

}

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

Ответ 2

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

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

    <Предварительно > <код > @Configuration открытый класс CorsConfig {   @Bean  public WebMvcConfigurer corsConfigurer() {       вернуть новый WebMvcConfigurerAdapter() {           @Override           public void addCorsMappings (реестр CorsRegistry) {               register.addMapping( "/**" ). allowedMethods ( "GET", "POST", "PUT", "DELETE" ). allowedOrigins ( "*" )                       .allowedHeaders( "*" );           }       };   } } Код >
  • Теперь, поскольку вы используете Spring Security, вам необходимо включить CORS на уровне Spring Security, чтобы позволить ему использовать конфигурацию, определенную на уровне Spring MVC, как:

    <Предварительно > <код > @EnableWebSecurity открытый класс WebSecurityConfig расширяет WebSecurityConfigurerAdapter {   @Override   protected void configure (HttpSecurity http) throws Exception {       http.cors(). и()...   } } Код >

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

Ответ 3

Если вы хотите включить CORS без использования фильтров или без файла конфигурации, просто добавьте

@CrossOrigin

в верхней части вашего контроллера, и он работает.

Ответ 4

Это работает для меня:

@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter  {
   //...
   @Override
   protected void configure(HttpSecurity http) throws Exception {

       //...         

       http.cors().configurationSource(new CorsConfigurationSource() {

        @Override
        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowedHeaders(Collections.singletonList("*"));
            config.setAllowedMethods(Collections.singletonList("*"));
            config.addAllowedOrigin("*");
            config.setAllowCredentials(true);
            return config;
        }
      });

      //...

   }

   //...

}

Ответ 5

Я использую spring boot 2.1.0 и у меня получилось

А. Добавьте сопоставления с помощью:

@Configuration
public class Config implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*");
    }
}

B. Добавьте ниже конфигурацию к моему HttpSecurity для безопасности весны

.cors().configurationSource(new CorsConfigurationSource() {

    @Override
    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedHeaders(Collections.singletonList("*"));
        config.setAllowedMethods(Collections.singletonList("*"));
        config.addAllowedOrigin("*");
        config.setAllowCredentials(true);
        return config;
    }
})

Также в случае прокси Zuul вы можете использовать эти INSTEAD OF A и B (просто используйте HttpSecurity.cors() чтобы включить его в безопасности Spring):

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

Ответ 6

Для меня единственная вещь, которая работала на 100%, когда использовалась весенняя безопасность, заключалась в том, чтобы пропустить весь дополнительный пух дополнительных фильтров и beans и любые косвенные "магические" люди, предлагающие работать с ними, но не для меня.

Вместо этого просто заставьте его написать нужные заголовки с помощью обычного StaticHeadersWriter:

<Предварительно > <код > @Configuration @EnableWebSecurity открытый класс SecurityConfig расширяет WebSecurityConfigurerAdapter {   @Override   protected void configure (HttpSecurity http) throws Exception {       HTTP           // ваш конфиг безопасности здесь           .authorizeRequests()           .antMatchers(HttpMethod.TRACE, "/**" ). denyAll()           .antMatchers( "/админ/**" ). проверка подлинности()           .anyRequest(). permitAll()           й(). httpBasic()           й(). (заголовки). frameOptions(). отключить()           й(). CSRF(). отключить()           .headers()           // заголовки, которые вы хотите здесь. Это решило все мои проблемы с CORS!           .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Origin", "*" ))           .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Methods", "POST, GET" ))           .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Max-Age", "3600" ))           .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Credentials", "true" ))           .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization" ) );   } } Код >

Это самый прямой и явный способ, который я нашел для этого. Надеюсь, это поможет кому-то.

Ответ 7

Чтобы опираться на другие ответы выше, в случае, если у вас есть приложение службы загрузки REST Spring (не Spring MVC) с безопасностью Spring, тогда достаточно включить CORS через безопасность Spring (если вы используете Spring MVC, то использование компонента WebMvcConfigurer как упомянуто Yogen, может это будет путь, поскольку Spring security делегирует приведенное в нем определение CORS)

Поэтому вам необходимо иметь конфигурацию безопасности, которая выполняет следующие действия:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    //other http security config
    http.cors().configurationSource(corsConfigurationSource());
}

//This can be customized as required
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    List<String> allowOrigins = Arrays.asList("*");
    configuration.setAllowedOrigins(allowOrigins);
    configuration.setAllowedMethods(singletonList("*"));
    configuration.setAllowedHeaders(singletonList("*"));
    //in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

}

Эта ссылка содержит больше информации о том же: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors

Замечания:

  1. Включение CORS для всех источников (*) для развернутого приложения может не всегда быть хорошей идеей.
  2. CSRF может быть включен с помощью настройки Spring HttpSecurity без каких-либо проблем
  3. Если у вас включена аутентификация в приложении с помощью Spring (например, через UserDetailsService), то configuration.setAllowCredentials(true); должен быть добавлен

Протестировано для загрузки Spring 2.0.0.RELEASE (то есть Spring 5.0.4.RELEASE и безопасности Spring 5.0.3.RELEASE)

Ответ 8

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

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

Ответ 9

Расширение класса WebSecurityConfigurerAdapter и переопределение метода configure() в вашем классе @EnableWebSecurity будет работать: Ниже приведен пример класса

@Override
protected void configure(final HttpSecurity http) throws Exception {

         http
        .csrf().disable()
        .exceptionHandling();
         http.headers().cacheControl();

        @Override
        public CorsConfiguration getCorsConfiguration(final HttpServletRequest request) {
            return new CorsConfiguration().applyPermitDefaultValues();
        }
    });
   }
}

Ответ 10

Если изначально ваша программа не использует spring безопасность и не может позволить себе для изменения кода, создание простого обратного прокси-сервера может сделать трюк. В моем случае я использовал Nginx со следующей конфигурацией:

  http { сервер {   слушайте 9090;   место нахождения /{     if ($ request_method = 'OPTIONS') {     add_header 'Access-Control-Allow-Origin' '*';     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';     #     # Пользовательские заголовки и заголовки различных браузеров * должны быть в порядке, но не     #     add_header 'DNT, User-Agent, Access-Control-Allow-Headers', X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range ';     #     # Сообщите клиенту, что эта предполетная информация действительна в течение 20 дней     #     add_header 'Access-Control-Max-Age' 1728000;     add_header 'Content-Type' 'text/plain; кодировка = UTF-8';     add_header 'Content-Length' 0;     return 204;     }     if ($ request_method = 'POST') {     add_header 'Access-Control-Allow-Origin' '*';     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';     add_header 'DNT, User-Agent, Access-Control-Allow-Headers', X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range ';     add_header 'Content-Length-Content-Range' Access-Control-Expose-Headers ';     }     if ($ request_method = 'GET') {     add_header 'Access-Control-Allow-Origin' '*';     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';     add_header 'DNT, User-Agent, Access-Control-Allow-Headers', X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range ';     add_header 'Content-Length-Content-Range' Access-Control-Expose-Headers ';     }
     proxy_pass http://localhost: 8080;   } }
}
Код>

Моя программа прослушивает : 8080.

REF: CORS на Nginx

Ответ 11

Это то, что сработало для меня.

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.cors();
    }

}

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
            .addMapping("/**")
            .allowedMethods("*")
            .allowedHeaders("*")
            .allowedOrigins("*")
            .allowCredentials(true);
    }

}