origins example baeldung application allow all angularjs rest spring-mvc spring-boot cors

angularjs - example - spring boot cors allow all origins



CORS con arranque de resorte y angularjs no funcionan (11)

Estoy tratando de llamar a los puntos finales REST en una aplicación (aplicación spring-boot) desde otra (angularjs). Las aplicaciones se ejecutan en los siguientes hosts y puertos.

  • Aplicación REST, utilizando arranque de primavera, http://localhost:8080
  • Aplicación HTML, utilizando angularjs, http://localhost:50029

También estoy usando spring-security con la aplicación spring-boot. Desde la aplicación HTML, puedo autenticarme en la aplicación REST, pero, a partir de entonces, aún no puedo acceder a ningún punto final REST. Por ejemplo, tengo un servicio angularjs definido de la siguiente manera.

adminServices.factory(''AdminService'', [''$resource'', ''$http'', ''conf'', function($resource, $http, conf) { var s = {}; s.isAdminLoggedIn = function(data) { return $http({ method: ''GET'', url: ''http://localhost:8080/api/admin/isloggedin'', withCredentials: true, headers: { ''X-Requested-With'': ''XMLHttpRequest'' } }); }; s.login = function(username, password) { var u = ''username='' + encodeURI(username); var p = ''password='' + encodeURI(password); var r = ''remember_me=1''; var data = u + ''&'' + p + ''&'' + r; return $http({ method: ''POST'', url: ''http://localhost:8080/login'', data: data, headers: {''Content-Type'': ''application/x-www-form-urlencoded''} }); }; return s; }]);

El controlador angularjs tiene el siguiente aspecto.

adminControllers.controller(''LoginController'', [''$scope'', ''$http'', ''AdminService'', function($scope, $http, AdminService) { $scope.username = ''''; $scope.password = ''''; $scope.signIn = function() { AdminService.login($scope.username, $scope.password) .success(function(d,s) { if(d[''success'']) { console.log(''ok authenticated, call another REST endpoint''); AdminService.isAdminLoggedIn() .success(function(d,s) { console.log(''i can access a protected REST endpoint after logging in''); }) .error(function(d, s) { console.log(''huh, error checking to see if admin is logged in''); $scope.reset(); }); } else { console.log(''bad credentials?''); } }) .error(function(d, s) { console.log(''huh, error happened!''); }); }; }]);

En la llamada a http://localhost:8080/api/admin/isloggedin , recibo un 401 Unauthorized .

En el lado de la aplicación REST, tengo un filtro CORS que se parece a lo siguiente.

@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CORSFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; response.setHeader("Access-Control-Allow-Origin", "http://localhost:50029"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token"); response.setHeader("Access-Control-Allow-Credentials", "true"); if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) { chain.doFilter(req, res); } } @Override public void init(FilterConfig config) throws ServletException { } }

Mi configuración de seguridad de primavera se parece a la siguiente.

@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private JsonAuthSuccessHandler jsonAuthSuccessHandler; @Autowired private JsonAuthFailureHandler jsonAuthFailureHandler; @Autowired private JsonLogoutSuccessHandler jsonLogoutSuccessHandler; @Autowired private AuthenticationProvider authenticationProvider; @Autowired private UserDetailsService userDetailsService; @Autowired private PersistentTokenRepository persistentTokenRepository; @Value("${rememberme.key}") private String rememberMeKey; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint(restAuthenticationEntryPoint) .and() .authorizeRequests() .antMatchers("/api/admin/**").hasRole("ADMIN") .antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .successHandler(jsonAuthSuccessHandler) .failureHandler(jsonAuthFailureHandler) .permitAll() .and() .logout() .deleteCookies("remember-me", "JSESSIONID") .logoutSuccessHandler(jsonLogoutSuccessHandler) .permitAll() .and() .rememberMe() .userDetailsService(userDetailsService) .tokenRepository(persistentTokenRepository) .rememberMeCookieName("REMEMBER_ME") .rememberMeParameter("remember_me") .tokenValiditySeconds(1209600) .useSecureCookie(false) .key(rememberMeKey); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .authenticationProvider(authenticationProvider); } }

Todo lo que están haciendo los controladores es escribir una respuesta JSON como {success: true} función de si el usuario inició sesión, no pudo autenticarse o cerró la sesión. RestAuthenticationEntryPoint tiene el siguiente aspecto.

@Component public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex) throws IOException, ServletException { resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } }

¿Alguna idea sobre lo que me falta o estoy haciendo mal?


Esto es lo que funcionó para mí.

@EnableWebSecurity public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors(); } } @Configuration public class WebConfiguration implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry .addMapping("/**") .allowedMethods("*") .allowedHeaders("*") .allowedOrigins("*") .allowCredentials(true); } }


Esto funciona para mi:

@Configuration public class MyConfig extends WebSecurityConfigurerAdapter { //... @Override protected void configure(HttpSecurity http) throws Exception { //... http.cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } }); //... } //... }


Estoy usando spring boot 2.1.0 y lo que funcionó para mí fue

A. Agregar asignaciones de cors por:

@Configuration public class Config implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } }

B. Agregue la siguiente configuración a mi HttpSecurity para la seguridad de primavera

.cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } })

También en el caso de un proxy Zuul, puede usar este EN LUGAR DE A y B (solo use HttpSecurity.cors() para habilitarlo en Spring Security):

@Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("OPTIONS"); config.addAllowedMethod("HEAD"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); config.addAllowedMethod("DELETE"); config.addAllowedMethod("PATCH"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }


Extender la clase WebSecurityConfigurerAdapter y anular el método configure () en su clase @EnableWebSecurity funcionaría: a continuación se muestra la clase de muestra

@Override protected void configure(final HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling(); http.headers().cacheControl(); @Override public CorsConfiguration getCorsConfiguration(final HttpServletRequest request) { return new CorsConfiguration().applyPermitDefaultValues(); } }); } }


Había estado en una situación similar. Después de investigar y probar, aquí están mis hallazgos:

  1. Con Spring Boot, la forma recomendada de habilitar CORS global es declarar dentro de Spring MVC y combinarlo con la configuración detallada de @CrossOrigin como:

    @Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*") .allowedHeaders("*"); } }; } }

  2. Ahora, dado que está utilizando Spring Security, también debe habilitar CORS en el nivel de Spring Security para permitirle aprovechar la configuración definida en el nivel de Spring MVC como:

    @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and()... } }

    Here hay un excelente tutorial que explica el soporte de CORS en Spring MVC framework.


Para construir sobre otras respuestas anteriores, en caso de que tenga una aplicación de servicio REST de arranque Spring (no Spring MVC) con seguridad Spring, entonces habilitar CORS a través de la seguridad Spring es suficiente (si usa Spring MVC, entonces usar un bean WebMvcConfigurer como lo menciona Yogen podría sea ​​el camino a seguir ya que Spring Security delegará a la definición CORS mencionada allí)

Por lo tanto, debe tener una configuración de seguridad que haga lo siguiente:

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { //other http security config http.cors().configurationSource(corsConfigurationSource()); } //This can be customized as required CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); List<String> allowOrigins = Arrays.asList("*"); configuration.setAllowedOrigins(allowOrigins); configuration.setAllowedMethods(singletonList("*")); configuration.setAllowedHeaders(singletonList("*")); //in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }

}

Este enlace tiene más información sobre el mismo: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors

Nota:

  1. Habilitar CORS para todos los orígenes (*) para una aplicación prod desplegada puede no ser siempre una buena idea.
  2. CSRF se puede habilitar a través de la personalización Spring HttpSecurity sin ningún problema
  3. En caso de que tenga habilitada la autenticación en la aplicación con Spring (a través de un UserDetailsService por ejemplo), entonces la configuration.setAllowCredentials(true); debe ser agregado

Probado para Spring boot 2.0.0.RELEASE (es decir, Spring 5.0.4.RELEASE y Spring security 5.0.3.RELEASE)


Para mí, lo único que funcionó al 100% cuando se usó seguridad de primavera fue omitir toda la pelusa adicional de filtros y frijoles adicionales y cualquier cosa "mágica" indirecta que la gente sugiriera que funcionó para ellos, pero no para mí.

En lugar de eso, simplemente forzarlo a escribir los encabezados que necesita con un StaticHeadersWriter simple:

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // your security config here .authorizeRequests() .antMatchers(HttpMethod.TRACE, "/**").denyAll() .antMatchers("/admin/**").authenticated() .anyRequest().permitAll() .and().httpBasic() .and().headers().frameOptions().disable() .and().csrf().disable() .headers() // the headers you want here. This solved all my CORS problems! .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "*")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "POST, GET")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Max-Age", "3600")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization")); } }

Esta es la forma más directa y explícita que encontré para hacerlo. Espero que ayude a alguien.


Si desea habilitar CORS sin usar filtros o sin el archivo de configuración, simplemente agregue

@CrossOrigin

a la parte superior de su controlador y funciona.


Si originalmente su programa no usa seguridad de primavera y no puede permitirse un cambio de código, crear un proxy inverso simple puede ser la solución. En mi caso, usé Nginx con la siguiente configuración:

http { server { listen 9090; location / { if ($request_method = ''OPTIONS'') { add_header ''Access-Control-Allow-Origin'' ''*''; add_header ''Access-Control-Allow-Methods'' ''GET, POST, OPTIONS''; # # Custom headers and headers various browsers *should* be OK with but aren''t # add_header ''Access-Control-Allow-Headers'' ''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range''; # # Tell client that this pre-flight info is valid for 20 days # add_header ''Access-Control-Max-Age'' 1728000; add_header ''Content-Type'' ''text/plain; charset=utf-8''; add_header ''Content-Length'' 0; return 204; } if ($request_method = ''POST'') { add_header ''Access-Control-Allow-Origin'' ''*''; add_header ''Access-Control-Allow-Methods'' ''GET, POST, OPTIONS''; add_header ''Access-Control-Allow-Headers'' ''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range''; add_header ''Access-Control-Expose-Headers'' ''Content-Length,Content-Range''; } if ($request_method = ''GET'') { add_header ''Access-Control-Allow-Origin'' ''*''; add_header ''Access-Control-Allow-Methods'' ''GET, POST, OPTIONS''; add_header ''Access-Control-Allow-Headers'' ''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range''; add_header ''Access-Control-Expose-Headers'' ''Content-Length,Content-Range''; } proxy_pass http://localhost:8080; } } }

Mi programa escucha : 8080 .

REF: CORS en Nginx


revisa este:

@Override protected void configure(HttpSecurity httpSecurity) throws Exception { ... .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() ... }


import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleCORSFilter implements Filter { private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class); public SimpleCORSFilter() { log.info("SimpleCORSFilter init"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }

No es necesario definir este filtro adicional simplemente agregue esta clase. Spring escaneará y lo agregará por usted. SimpleCORSFilter. Aquí está el ejemplo: spring-enable-cors