jquery - Problema de CORS: no hay un encabezado ''Access-Control-Allow-Origin'' presente en el recurso solicitado
spring tomcat (3)
Desde Spring Security 4.1, esta es la forma correcta de hacer que Spring Security sea compatible con CORS (también se necesita en 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");
}
}
y:
@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;
}
}
No haga nada de lo siguiente, que son la forma incorrecta de intentar resolver el problema:
-
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
-
web.ignoring().antMatchers(HttpMethod.OPTIONS);
Referencia: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
He creado dos aplicaciones web: aplicaciones de cliente y de servicio.
La interacción entre las aplicaciones de cliente y servicio funciona bien cuando se implementan en la misma instancia de Tomcat.
Pero cuando las aplicaciones se implementan en instancias separadas de Tomcat (máquinas diferentes), recibo el siguiente error cuando solicito la aplicación de servicio enviada.
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
La aplicación Mi cliente utiliza JQuery, HTML5 y Bootstrap.
La llamada AJAX se realiza al servicio como se muestra a continuación:
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 );
});
}
Mi aplicación de servicio utiliza Spring MVC, Spring Data JPA y Spring Security.
He incluido la clase
CorsConfiguration
como se muestra a continuación:
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;
}
}
Dependencias de 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>
Estoy usando el servidor Apache Tomcat para la implementación.
En mi caso, tengo el servidor de recursos con la seguridad de OAuth habilitada y ninguna de las soluciones anteriores funcionó. Después de un poco de depuración y googleo descubrí por qué.
@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;
}
Básicamente, en este ejemplo,
Ordered.HIGHEST_PRECEDENCE
es clave.
https://github.com/spring-projects/spring-security-oauth/issues/938
Varias dependencias de pom agregan diferentes tipos de filtros y, por lo tanto, podríamos tener problemas según el orden.
La solicitud de verificación previa de CORS utiliza
OPTIONS
HTTP sin credenciales, consulte
Intercambio de recursos de origen cruzado
:
De lo contrario, haga una solicitud de verificación previa . Obtenga la URL de solicitud del origen de origen utilizando la fuente de referencia como fuente de referencia de anulación con el indicador de redireccionamiento manual y el conjunto de indicadores de bloqueo de cookies, utilizando el método OPTIONS, y con las siguientes restricciones adicionales:
- Incluya un encabezado Método de solicitud de control de acceso con el valor del campo de encabezado como método de solicitud (incluso cuando sea un método simple).
- Si los encabezados de solicitud de autor no están vacíos, incluya un encabezado de Access-Control-Request-Headers con un valor de campo de encabezado, una lista separada por comas de los nombres de campo de encabezado de los encabezados de solicitud de autor en orden lexicográfico, cada uno convertido a ASCII en minúscula (incluso cuando uno o más son un encabezado simple).
- Excluir los encabezados de solicitud de autor.
- Excluir credenciales de usuario.
- Excluir el cuerpo de la entidad de solicitud.
Debe permitir el acceso anónimo para las
OPTIONS
HTTP.
Su código modificado (y simplificado):
@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();
}
Desde Spring Security 4.2.0 puede usar el soporte incorporado, consulte la Referencia de Spring Security :
19. CORS
Spring Framework proporciona soporte de primera clase para CORS. CORS debe procesarse antes de Spring Security porque la solicitud previa al vuelo no contendrá ninguna cookie (es decir, el
JSESSIONID
). Si la solicitud no contiene cookies y Spring Security es lo primero, determinará que el usuario no está autenticado (ya que no hay cookies en la solicitud) y la rechazará.La manera más fácil de garantizar que CORS se maneje primero es usar el
CorsFilter
. Los usuarios pueden integrar elCorsFilter
con Spring Security al proporcionar unCorsConfigurationSource
utilizando lo siguiente:
@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; } }