Spring Boot Security CORS
spring-boot spring-mvc (7)
Tengo un problema con el filtro CORS en las URL de seguridad de primavera.
No establece
Access-Control-Allow-Origin
y otros encabezados expuestos en las URL que pertenecen a spring sec (inicio / cierre de sesión) o filtrados por Spring Security.
Aquí están las configuraciones.
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);
}
}
Seguridad:
@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'')");
}
}
Entonces, si hago una solicitud a las URL que no son escuchadas por la seguridad, se configuran los encabezados CORS. URL de seguridad de Spring: no configurada.
Spring boot 1.4.1
Opción 1 (Usar WebMvcConfigurer bean):
La configuración CORS con la que comenzó no es la forma correcta de hacerlo con Spring Boot.
WebMvcConfigurer
registrar un bean
WebMvcConfigurer
.
Referencia
here
Ejemplo de configuración de Spring Boot 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");
}
};
}
}
Esto proporcionará la configuración CORS para una aplicación Spring Boot básica (sin iniciador de seguridad). Tenga en cuenta que el soporte CORS existe independientemente de Spring Security.
Una vez que introduce Spring Security, debe registrar CORS con su configuración de seguridad. Spring Security es lo suficientemente inteligente como para recoger su configuración CORS existente.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
....
Opción 2 (Usar el bean CorsConfigurationSource):
La primera opción que describí es realmente desde la perspectiva de agregar Spring Security a una aplicación existente. Si está agregando Spring Security desde el primer momento, la forma que se describe en los Documentos de Spring Security involucra agregar un 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;
}
}
Acabo de tener un problema similar, estaba tratando de ejecutar una solicitud desde mi interfaz en React ejecutándose en http: // localhost: 3000 , a mi backend en SpringBoot ejecutándose en http: // localhost: 8080 . Tuve dos errores:
Control de acceso Permitir origen
Resolví esto muy fácilmente agregando esto a mi RestController:
@CrossOrigin(origins = ["http://localhost:3000"])
Después de solucionar esto, comencé a recibir este error: el valor del encabezado ''Access-Control-Allow-Credentials'' en la respuesta es '''', que debe ser ''verdadero''
Acceso-Control-Permitir-Credenciales
Este puede solucionarse de dos maneras:
-
Agregar
allowCredentials = "true"
a la configuración de CrossOrigin:@CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")
-
Cambiar las opciones de credenciales de la búsqueda en la solicitud de interfaz. Básicamente, deberá realizar la llamada de recuperación de esta manera:
fetch(''http://localhost:8080/your/api'', { credentials: ''same-origin'' })
Espero que esto ayude =)
Actualmente, las solicitudes de OPTIONS están bloqueadas de forma predeterminada si la seguridad está habilitada.
Simplemente agregue un bean adicional y las solicitudes de verificación previa se manejarán correctamente:
@Bean
public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
return configurer -> {
List<RequestMatcher> matchers = new ArrayList<>();
matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
configurer.requestMatchers(new OrRequestMatcher(matchers));
};
}
Tenga en cuenta que, dependiendo de su aplicación, esto puede abrirlo para posibles exploits.
Problema abierto para una mejor solución: https://github.com/spring-projects/spring-security/issues/4448
En lugar de utilizar CorsRegistry, puede escribir su propio CorsFilter y agregarlo a su configuración de seguridad.
Clase CorsFilter personalizada:
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() {
}
}
Clase de configuración de seguridad:
@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'')");
}
}
Si lo necesita para un desarrollo local rápido, simplemente agregue esta anotación en su controlador. (fuera de curso cambiar los orígenes según sea necesario)
@ResponseStatus (
value = HttpStatus.NO_CONTENT
)
public class CorsException extends RuntimeException
{
}
También puede lograr esto con un interceptor.
Use la excepción para asegurarse de que está finalizando el ciclo de vida de la solicitud:
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);
}
}
Luego, en su interceptor, establezca encabezados para todas las solicitudes de OPTIONS y arroje la excepción:
@Configuration
public class MiddlewareConfig extends WebMvcConfigurerAdapter
{
@Override
public void addInterceptors (InterceptorRegistry registry)
{
registry.addInterceptor(new CorsMiddleware())
.addPathPatterns("/**");
}
}
Por último, aplique el interceptor a todas las rutas:
@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
Tengo un cliente web basado en
React
, y mi API REST de back-end está ejecutando
Spring Boot
Ver 1.5.2
Quería habilitar
CORS
rápidamente en todas las solicitudes de ruta del controlador de mi cliente que se ejecuta en
localhost:8080
.
Dentro de mi configuración de seguridad, simplemente agregué un
@Bean
de tipo
FilterRegistrationBean
y lo
FilterRegistrationBean
funcionar fácilmente.
Aquí está el código:
@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**
....
}
....
....
}
Puede consultar los documentos de Spring Boot aquí