Spring Boot Security CORS

У меня проблема с фильтром CORS на URL-адресах безопасности spring. Он не устанавливает Access-Control-Allow-Origin и другой открытый заголовок на URL, принадлежащий spring sec (логин/выход) или фильтруется с помощью spring Безопасность.

Вот конфигурации.

CORS:

@Configuration
@EnableWebMvc
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
********some irrelevant configs************
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT")
                .allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
                        "Access-Control-Request-Headers")
                .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
                .allowCredentials(true).maxAge(3600);
    }
}

Безопасность:

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .formLogin()
                    .successHandler(ajaxSuccessHandler)
                    .failureHandler(ajaxFailureHandler)
                    .loginProcessingUrl("/authentication")
                    .passwordParameter("password")
                    .usernameParameter("username")
                .and()
                .logout()
                    .deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true)
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/")
                .and()
                .csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/authentication").permitAll()
                .antMatchers("/oauth/token").permitAll()
                .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/user/*").access("hasRole('ROLE_USER')");
    }
}

Итак, если я делаю запрос к URL-адресу, который не прослушивается с помощью безопасности, то устанавливаются заголовки CORS. spring URL-адрес безопасности - не установлен.

Spring boot 1.4.1

Ответ 1

Вместо использования CorsRegistry вы можете написать свой собственный CorsFilter и добавить его в свою конфигурацию безопасности.

Пользовательский класс CorsFilter:

public class CorsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request= (HttpServletRequest) servletRequest;

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", true);
        response.setHeader("Access-Control-Max-Age", 180);
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

Класс безопасности:

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    CorsFilter corsFilter() {
        CorsFilter filter = new CorsFilter();
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(corsFilter(), SessionManagementFilter.class) //adds your custom CorsFilter
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .formLogin()
                    .successHandler(ajaxSuccessHandler)
                    .failureHandler(ajaxFailureHandler)
                    .loginProcessingUrl("/authentication")
                    .passwordParameter("password")
                    .usernameParameter("username")
                .and()
                .logout()
                    .deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true)
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/")
                .and()
                .csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/authentication").permitAll()
                .antMatchers("/oauth/token").permitAll()
                .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/user/*").access("hasRole('ROLE_USER')");
    }
}

Ответ 2

Вариант 1 (используйте WebMvcConfigurer bean):

Конфигурация CORS, с которой вы начали, - это не правильный способ сделать это с помощью Spring Boot. Вам необходимо зарегистрировать WebMvcConfigurer bean. Ссылка здесь.

Пример Spring Конфигурация загрузки CORS:

@Configuration
@Profile("dev")
public class DevConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("http://localhost:4200");
            }
        };
    }

}

Это обеспечит конфигурацию CORS для базового (без запуска безопасности) Spring приложения для загрузки. Обратите внимание: поддержка CORS не зависит от Spring безопасности.

После введения Spring безопасности вам необходимо зарегистрировать CORS с вашей конфигурацией безопасности. Spring Безопасность достаточно умна, чтобы забрать существующую конфигурацию CORS.

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

Вариант 2 (используйте CorsConfigurationSource bean):

Первый вариант, который я описал, действительно с точки зрения добавления Spring Security в существующее приложение. Если вы добавляете Spring Security из get-go, способ, описанный в Spring Документах по безопасности, включает добавление CorsConfigurationSource bean.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors().and()
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Ответ 3

У меня есть веб-клиент на основе React, а мой backstream REST API работает Spring Boot Ver 1.5.2

Я хотел бы быстро включить CORS во все запросы маршрута контроллера от моего клиента, работающего на localhost:8080. В моей конфигурации безопасности я просто добавил @Bean типа FilterRegistrationBean и легко работал.

Вот код:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthConfiguration extends WebSecurityConfigurerAdapter {

....
....

  @Bean
  public FilterRegistrationBean corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin(corsAllowedOrigin); // @Value: http://localhost:8080
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;
  }

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

....
....

}

Вы можете сослаться на Spring Boot docs here

Ответ 4

Если вам это нужно для быстрой локальной разработки, просто добавьте эту аннотацию на свой контроллер. (вне зависимости от источника происхождения по мере необходимости)

@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)

Ответ 5

Вы также можете достичь этого с помощью перехватчика.

Используйте исключение, чтобы гарантировать завершение жизненного цикла запроса:

@ResponseStatus (
    value = HttpStatus.NO_CONTENT
)
public class CorsException extends RuntimeException
{
}

Затем в вашем перехватчике установите заголовки для всех запросов OPTIONS и выбросите исключение:

public class CorsMiddleware extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle (
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler
    ) throws Exception
    {
        if (request.getMethod().equals("OPTIONS")) {
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Credentials", "true");
            response.addHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS, DELETE");
            response.addHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,Authorization,If-Modified-Since,Cache-Control,Content-Type");
            response.addHeader("Access-Control-Max-Age", "3600");
            response.addHeader("charset", "utf-8");
            throw new CorsException();
        }

        return super.preHandle(request, response, handler);
    }
}

Наконец, примените перехватчик ко всем маршрутам:

@Configuration
public class MiddlewareConfig extends WebMvcConfigurerAdapter
{
    @Override
    public void addInterceptors (InterceptorRegistry registry)
    {
        registry.addInterceptor(new CorsMiddleware())
                .addPathPatterns("/**");
    }
}

Ответ 6

У меня просто была похожая проблема, я пытался выполнить запрос от моего внешнего интерфейса в React, выполняющегося по адресу http://localhost: 3000, к моему бэкэнду в SpringBoot, выполняющемуся по адресу http://localhost: 8080. У меня было две ошибки:

Контроль доступа Разрешить Происхождение

Я решил это очень легко, добавив это в мой RestController:

@CrossOrigin(origins = ["http://localhost:3000"])

После исправления я начал получать эту ошибку: значение заголовка "Access-Control-Allow-Credentials" в ответе "", которое должно быть "истина"

Access-Control-Allow-Credentials

Это можно обойти двумя способами:

  1. Добавление allowCredentials = "true" в конфигурацию CrossOrigin:

    @CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")

  2. Изменение параметров доступа к выборке в запросе веб-интерфейса. По сути, вам нужно выполнить вызов fetch следующим образом:

    fetch('http://localhost:8080/your/api', { credentials: 'same-origin' })

Надеюсь, это поможет =)

Ответ 7

В настоящее время запросы OPTIONS блокируются по умолчанию, если включена защита.

Просто добавьте дополнительный bean, и запросы предварительной проверки будут обработаны правильно:

   @Bean
   public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
      return configurer -> {
         List<RequestMatcher> matchers = new ArrayList<>();
         matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
         configurer.requestMatchers(new OrRequestMatcher(matchers));
      };
   }

Обратите внимание, что в зависимости от вашего приложения это может открыть его для потенциальных эксплойтов.

Открытая проблема для лучшего решения: https://github.com/spring-projects/spring-security/issues/4448

Ответ 8

Шаг 1: Spring уже имеет CorsFilter, даже если вы можете просто зарегистрировать свой собственный CorsFilter в качестве компонента, чтобы обеспечить свою собственную конфигурацию.

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Provide list of origins if you want multiple origins
    config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
    config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"));
    config.setAllowCredentials(true);
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

Шаг 2: аннотируйте контроллер с помощью аннотации @CrossOrigin.