Spring Boot Security CORS

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

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

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. URL-адреса безопасности Spring - не установлены.

Пружинная загрузка 1.4.1

13 ответов

Решение

Вместо использования 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')");
    }
}

Вариант 1 (использовать компонент WebMvcConfigurer):

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

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

@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 Boot. Обратите внимание, что поддержка CORS существует независимо от Spring Security.

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

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

Вариант 2 (использовать компонент CorsConfigurationSource):

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

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

Это довольно чисто и не требует дополнительных настроек. Передайте звездочки там, где вы хотите, чтобы все параметры были действительными (как я сделал в setAllowedHeaders).

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

@Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.cors().configurationSource(request -> {
      var cors = new CorsConfiguration();
      cors.setAllowedOrigins(List.of("http://localhost:4200", "http://127.0.0.1:80", "http://example.com"));
      cors.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
      cors.setAllowedHeaders(List.of("*"));
      return cors;
    }).and()...
  }
}

У меня просто была похожая проблема, я пытался выполнить запрос от моего внешнего интерфейса в 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' })

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

У меня есть React веб-клиент, и мой бэкэнд REST API работает Spring Boot Версия 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 здесь

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

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

   @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

Если кто-то сталкивается с той же проблемой в 2023 году. Вот решение: проблема в том, что при работе с классом SecurityConfig мы должны настроить наши правила безопасности, чтобы разрешить необходимые запросы CORS, поэтому, если вы этого не сделаете, проверки Cors не будут обойдены. Даже вы настраиваете класс CorsConfig, который реализует WebMvcConfigurer, или используете аннотацию @CrossOrigin в своем контроллере, или добавляете метод с аннотацией @Bean в свой ApplicationClass, который разрешает запросы между источниками. В противном случае, если мы используем PermitAll() в наших правилах безопасности, это обходит безопасность. проверки, включая проверки CORS. Итак, чтобы решить эту проблему, нам нужно настроить наши правила безопасности, чтобы разрешить необходимые запросы CORS.

      @Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .csrf((csrf) -> csrf.disable())
            .cors(cors -> {
                cors.configurationSource(corsConfigurationSource());
            })
            .authorizeHttpRequests((auth) -> auth
                    .anyRequest()
                    .authenticated()
            )
            .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));

    return httpSecurity.build();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.addAllowedOrigin("http://localhost:3000"); 
    configuration.addAllowedMethod("*"); 
    configuration.addAllowedHeader("*"); 
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new 
     UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
}

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

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

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

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

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

Если вы используете Spring WebFlux, вам следует добавить следующий метод в класс вашего приложения, чтобы включить CORS:

          @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowedOrigins(Arrays.asList("*"));
        corsConfig.setMaxAge(3600L);
        corsConfig.addAllowedMethod("*");
        corsConfig.addAllowedHeader("Requestor-Type");
        corsConfig.addExposedHeader("X-Get-Header");

        UrlBasedCorsConfigurationSource source =
                new UrlBasedCorsConfigurationSource();

        source.registerCorsConfiguration("/**", corsConfig);

        return new CorsWebFilter(source);
    }

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

Шаг 1. Реализуйте ConfigurationClass

      package med.voll.api.infra.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebConfigCors implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*") // somente do servidor na porta 3000
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "TRACE", "CONNECT"); // métodos
                                                                                                        // permitidos;
    }
}

Шаг 2. Включите Cors в моем классе SecurityConfigurations.

          @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .cors().and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeHttpRequests()
                .requestMatchers(HttpMethod.GET, "/hello").permitAll()
                .requestMatchers(HttpMethod.POST, "/login").permitAll()
                // .requestMatchers(HttpMethod.GET, "/hello").permitAll()
                .anyRequest().authenticated()
                .and().addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

See that in the first line of httpSecurity param, i put .cors().and()

And thats works fine!

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

CorsFilter класс:

public class CorsFilter implements Filter {

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

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With");
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }
}

а затем снова настройка заголовков в классе, расширяющем WebSecurityConfigurerAdapter:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {

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

    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("Im configuring it");
        (
                (HttpSecurity)
                        (
                                (HttpSecurity)
                                        (
                                                (ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)
                                                        http
                                                                .headers().addHeaderWriter(
                                                                new StaticHeadersWriter("Access-Control-Allow-Origin", "*")).and()
                                                                .addFilterBefore(corsFilter(), SessionManagementFilter.class)
                                                                .csrf().disable()
                                                                .authorizeRequests()
                                                                .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
                                                                .anyRequest()

                                        ).authenticated().and()
                        ).formLogin().and()
        ).httpBasic();
    }

}

Я пробовал с конфигурацией ниже, и это сработало!

      @EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable().cors().configurationSource(configurationSource()).and()
        .requiresChannel()
        .anyRequest()
        .requiresSecure();   
}


private CorsConfigurationSource configurationSource() {
      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
      CorsConfiguration config = new CorsConfiguration();
      config.addAllowedOrigin("*");
      config.setAllowCredentials(true);
      config.addAllowedHeader("X-Requested-With");
      config.addAllowedHeader("Content-Type");
      config.addAllowedMethod(HttpMethod.POST);
      source.registerCorsConfiguration("/**", config);
      return source;
    }
 }
Другие вопросы по тегам