Проблема CORS - в запрашиваемом ресурсе отсутствует заголовок "Access-Control-Allow-Origin"
Я создал два веб-приложения - клиентские и сервисные приложения.
Взаимодействие между клиентскими и сервисными приложениями проходит нормально, когда они развернуты в одном экземпляре Tomcat.
Но когда приложения развертываются в отдельных экземплярах Tomcat (на разных машинах), я получаю следующую ошибку при запросе на отправку приложения-службы.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401
Мое клиентское приложение использует JQuery, HTML5 и Bootstrap.
AJAX вызов сделан на обслуживание, как показано ниже:
var auth = "Basic " + btoa({usname} + ":" + {password});
var service_url = {serviceAppDomainName}/services;
if($("#registrationForm").valid()){
var formData = JSON.stringify(getFormData(registrationForm));
$.ajax({
url: service_url+action,
dataType: 'json',
async: false,
type: 'POST',
headers:{
"Authorization":auth
},
contentType: 'application/json',
data: formData,
success: function(data){
//success code
},
error: function( jqXhr, textStatus, errorThrown ){
alert( errorThrown );
});
}
Мое сервисное приложение использует Spring MVC, Spring Data JPA и Spring Security.
Я включил CorsConfiguration
класс, как показано ниже:
CORSConfig.java
:
@Configuration
@EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
SecurityConfig.java
:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("authenticationService")
private UserDetailsService userDetailsService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
}
Зависимости Spring Security:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
Я использую сервер Apache Tomcat для развертывания.
12 ответов
Предварительный запрос CORS использует HTTP OPTIONS
без учетных данных, см. Обмен ресурсами между источниками:
В противном случае сделайте предварительный запрос. Извлечь URL-адрес запроса из источника источника источника, используя источник реферера в качестве источника источника переопределения с установленным флагом ручного перенаправления и флагом куки-файлов блока, используя метод OPTIONS и следующие дополнительные ограничения:
- Включите заголовок Access-Control-Request-Method со значением поля заголовка в метод запроса (даже если это простой метод).
- Если заголовки запроса автора не пусты, включите заголовок Access-Control-Request-Headers со значением поля заголовка в виде разделенного запятыми списка имен полей заголовка из заголовков запроса автора в лексикографическом порядке, каждый из которых преобразуется в нижний регистр ASCII (даже когда один или более простой заголовок).
- Исключить заголовки запроса автора.
- Исключить учетные данные пользователя.
- Исключить тело запроса.
Вы должны разрешить анонимный доступ для HTTP OPTIONS
,
Ваш измененный (и упрощенный) код:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.andMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
}
Начиная с Spring Security 4.2.0 вы можете использовать встроенную поддержку, см. Spring Security Reference:
19. CORS
Spring Framework обеспечивает первоклассную поддержку CORS. CORS должен быть обработан до Spring Security, так как предполетный запрос не будет содержать куки (т.е.
JSESSIONID
). Если запрос не содержит файлов cookie и Spring Security является первым, запрос определит, что пользователь не прошел проверку подлинности (поскольку в запросе нет файлов cookie), и отклонит его.Самый простой способ убедиться, что CORS обрабатывается первым - это использовать
CorsFilter
, Пользователи могут интегрироватьCorsFilter
с Spring Security, предоставляя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; } }
Начиная с Spring Security 4.1, это правильный способ заставить Spring Security поддерживать CORS (также необходим в Spring Boot 1.4/1.5):
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
}
а также:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable();
http.cors();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Не делайте ничего из нижеприведенного, что является неправильным способом решения проблемы:
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
web.ignoring().antMatchers(HttpMethod.OPTIONS);
Ссылка: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
Добавьте приведенную ниже конфигурацию в основное приложение. Это сработало у меня в весеннем загрузочном приложении 2.3.1
package com.example.restservicecors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class RestServiceCorsApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceCorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
};
}
}
Справочный источник : https://spring.io/guides/gs/rest-service-cors/
В моем случае у меня есть Resource Server с включенной защитой OAuth, и ни одно из перечисленных выше решений не работает. После некоторой отладки и поиска в Google понял, почему.
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
В основном в этом примере Ordered.HIGHEST_PRECEDENCE
это ключ!
https://github.com/spring-projects/spring-security-oauth/issues/938
Различные зависимости POM добавляют различные виды фильтров, и поэтому у нас могут быть проблемы в зависимости от порядка.
Если вы используете angular с загрузкой Spring. Это конфигурация, которая сработала для меня
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable()
.authorizeHttpRequests()
.requestMatchers(HttpMethod.OPTIONS).permitAll() // Angular httpClient uses OPTIONS first in all requests
.anyRequest()
.authenticated()
.and()
.httpBasic().and().headers().frameOptions().disable(); // For swagger
return http.build();
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
У меня была аналогичная проблема с Spring Security 6, поскольку она игнорировала мою конфигурацию CORS. Если вы хотите быть по-настоящему уверены, что SecurityConfig подберет вашу пользовательскую конфигурацию CorsConfiguration, вы также можете применить ее с помощью:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
...
.cors(cors -> cors.configurationSource(yourCustomCorsConfigurationSource))
...;
}
Поскольку ни один из этих опубликованных примеров мне не помог, я использовал свои собственные знания. Обычно самые сложные ошибки случаются со мной. Вот как я справился с этой ошибкой.
В этом методе:
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
CorsConfiguration по умолчанию имеет разрешенный метод:POST, HEAD, GET, поэтому PUT, DELETE НЕ БУДУТ РАБОТАТЬ! Я создал новый экземпляр CorsConfiguration и установил разрешенные методы:
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
так что теперь мой метод выглядит так:
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", cors.applyPermitDefaultValues());
return source;
}
и это работает как шарм. Надеюсь, это кому-нибудь поможет. Конечно, все остальные настройки выполняются с помощью документации spring.io.
Попробуй это:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
import java.util.List;
@Component
public class CorsFilterConfig {
public static final List<String> allowedOrigins = Arrays.asList("*");
@Bean
public FilterRegistrationBean<CorsFilter> initCorsFilter() {
// @formatter:off
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
config.addAllowedMethod("*");
config.setAllowedOrigins(allowedOrigins);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
// @formatter:on
}
}
В моем случае я использую дистрибутив CloudFront для кода пользовательского интерфейса, развернутого на S3. Из S3 запросы кода пользовательского интерфейса передаются на сервер API, и я столкнулся с той же ошибкой в консоли браузера. Это вводило в заблуждение, указывая на проблемы с CORS, но основной причиной была неспособность CloudFront подключиться к исходному серверу. Поэтому для решения этой проблемы важно проверить работоспособность и доступность исходного сервера.
Вы легко можете добавить@CrossOrigin
аннотация, чтобы разрешить их все, если вы используетеUsernamePasswordAuthenticationFilter
. И в настройках безопасностиhttp.cors().and()
. Это сработало для меня.
@CrossOrigin(origins = "*")
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManagerBean());
customAuthenticationFilter.setFilterProcessesUrl("/api/login");
http
.csrf().disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// We can ant match out paths to the corresponding roles --> we allow certain roles to access certain API's
http
.cors()
.and();
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**").permitAll();
...
Это сработало для: spring-boot-starter-parent 2.2.6.RELEASE
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
}
Замените "*" на что-нибудь значимое в продукте
Добавьте приведенную ниже конфигурацию в основное приложение. Это сработало у меня в весеннем загрузочном приложении 2.3.1
package com.example.restservicecors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class RestServiceCorsApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceCorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
};
}
}
Справочный источник: https://spring.io/guides/gs/rest-service-cors/
Другой подход, если вы хотите переместить этот код в файл конфигурации.
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 WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
}