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
Это можно обойти двумя способами:
-
Добавление allowCredentials = "true"
в конфигурацию CrossOrigin:
@CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")
-
Изменение параметров доступа к выборке в запросе веб-интерфейса. По сути, вам нужно выполнить вызов 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
.