JS access to Spring REST services behind ZUUL blocked by CORS policy - spring-boot

I am putting together a small service-based platform using various Spring Cloud frameworks. The individual components are as follows:
an Eureka discovery server
a Spring-Config server
an Authentication server
a Zuul gateway server
a REST service
a PostgreSQL server
a simple JQuery-based client running in the browser
(yes this is based on the design presented in Manning's "Spring Microservices in Action")
This all works just fine in development. However I have recently deployed all this server-side stuff to an external server (running docker-compose) and I can no longer access the service endpoints.
When calling the service endpoints via JQuery I get the following error:
Access to XMLHttpRequest at 'https://my.domain.com/api/resource/123/subresource/456' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have tried adding the following to the Zuul server (in the same #EnableZuulProxy Spring boot application class) :
#Bean
public FilterRegsitrationBean corsFilter() {
UrlBaseCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("*", config);
FilterRegistrationBean<CorsFilter> bean =
new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
This yielded the same error.
Any ideas?

In Controller, you need to add another annotation with #RestController/#Controller.
#CrossOrigin
Like this :
#Controller
#CrossOrigin(origins = "*", allowedHeaders = "*")
public class HomeController
Try this one.

OK so I fixed it by removing all Cors configs from the services and added this to the Zuul gateway.
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("POST", "PUT", "GET", "OPTIONS", "DELETE", "HEAD");
}
}
It works but I have no idea why my previous attempts don't (and I obviously hate fixing something without having understood how). So if anyone has any ideas...
Cheers

Related

Spring Security CORS Blocked By Policy

This should be easy, but of course since it's Spring Security, it's not.
I am attempting to access a relatively simple api running as a Spring Boot application from an Angular application. Angular makes the calls to the API just fine, but the backend blocks the request due to CORS policy:
I added the following to my Security configuration:
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://localtest.me:4200","http://localtest.me:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
#Profile("dev")
public SecurityFilterChain devFilterChain(HttpSecurity http) throws Exception {
// define a custom filter, irrelevant to question
// #formatter:off
http
.addFilterAfter(filter, ConcurrentSessionFilter.class)
.authorizeRequests()
.antMatchers("/path1","/path2","/logout").permitAll()
.anyRequest().authenticated()
.and()
.cors();
// #formatter:on
return http.build();
}
This STILL does not prevent the CORS policy block.
I've also tried putting various iterations of #CrossOrigin (with and without origins argument):
on the Controller class
on the endpoint method itself
Am I making a simple error causing this?
Edit: I added breakpoints to Spring's CorsFilter, and they are not being hit.
Edit: Adding screenshot by request:
try to add this at the head ( beggining of your controller)
#CrossOrigin(origins = "http://localhost:{youy_angular_application_port}")
public class YourRestController {
}
Not the proudest and most beautiful solution, but some months ago, I also needed to expose some endpoints to my frontend, so my angular application could send requests to them.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/user").allowedOrigins("http://localhost:4200");
registry.addMapping("/post").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/").allowedOrigins("http://localhost:4200");
registry.addMapping("/user/{id}").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/{id}").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/user").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/user/").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/user/{id}").allowedOrigins("http://localhost:4200");
registry.addMapping("/user/").allowedOrigins("http://localhost:4200");
}
};
}
The bean can get implemented where ever, since its a bean. In my case I implemented it in the MainApplication.java class.
Okay, here's what happened.
At end-of-day the day before yesterday, some numbskull checked in a change to application.properties changing the context-root of the application.
The application was no longer being served at http://localtest.me:8000/api , it was being servered at http://localtest.me:8000/appname/api.
Effectively, I had a 404 error as much as I had a CORS error. Chrome didn't tell me that the path didn't exist, it just kept telling me it was blocked.

No 'Access-Control-Allow-Origin' header is present on the requested resource (Spring)

I know there are a lot threads on the forum about this issue but still haven't figure out a solution.
So, I have deployed two applications in a private JVM/tomcat 8.5.30 on my vps. The one is my ROOT.war and the other one is the admin.war They were accesible from http://example.com and http://example.com/admin
Before I installed a ssl certificate everything worked fine. After installing it and forcing https redirect I am facing a problem with my admin.war (now they are both accesible from https://example.com and https://example.com/admin)
My admin works with a lot of jquery (I cannot change that) and I am getting this error every time I am trying to submit something
Access to XMLHttpRequest at 'http: //example.com/admin/add' from origin 'https: //example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
So I am trying to fix this via spring security. In my security configuration I have
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SiteSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
//.....
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers"));
configuration.addExposedHeader("Access-Control-Allow-Headers");
configuration.setAllowedMethods(Arrays.asList("POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
I do this for both my root app and my admin app ( I don't know if that's correct to do it for both of them). Still doesn't work.
Any help?
thanks!!
If you see Error
'http://example.com/admin/add' from origin 'https://example.com' has been blocked
There are 2 issues
1 I guess your /add API call is not getting redirected to https. Ideally it should be https://example.com/admin/add Either you resolve this
or
2 Change setAllowedOrigins in your Admin App to http as well like this
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com", "http://example.com"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers"));
configuration.addExposedHeader("Access-Control-Allow-Headers");
configuration.setAllowedMethods(Arrays.asList("POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

Spring CORS Filter problem in light of Spring Cloud microservices with Zuul

Given: angular frontend application sends requests to backend microservice through gateway microservice. The backend is in Spring Cloud.
Question: how to correctly configure CORS filters to get rid of the following error:
Access to XMLHttpRequest at 'http://gateway-service:5555/api/useful-service/myentities/' from origin 'http://localhost:4200' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, http://localhost:4200', but only one is allowed.
That's what I have written so far:
Gateway Service
My main class in the gateway serivice has 3 annotations: #SpringBootApplication, #EnableZuulProxy and #Configuration. So as I don't confgigured any security thing I presume that the Spring Security is not being used therefore I need to configure Spring MVC's CorsFilter. I do that like this (comments are for future searchers):
#Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowCredentials(true);
//corsConfig.addAllowedOrigin("http://localhost:4200");
corsConfig.addAllowedOrigin("*"); //wildcard that will simply copy the value of the request's Origin header
// into the value of the Response's Access-Control-Allow-Origin header, effectively allowing all origins.
// You can add specific origins instead if you wish to limit them.
corsConfig.addAllowedHeader("*");
corsConfig.addAllowedMethod("OPTIONS");
corsConfig.addAllowedMethod("HEAD");
corsConfig.addAllowedMethod("GET");
corsConfig.addAllowedMethod("POST");
corsConfig.addAllowedMethod("PUT");
corsConfig.addAllowedMethod("DELETE");
corsConfig.addAllowedMethod("PATCH");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", corsConfig);
return new CorsFilter(configSource);
}
Useful Service
The main class over here is annotated with #EnableResourceServer and #SpringBootApplication. According to my "business rules" I would like to have Spring authorization (url security, and in the future the method security also) so as I configured Spring Security in general and OAuth2 and in partucular I should configure security's cors filter as well. Here's relevant security snippet that enables cors:
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.cors(); // by default uses a Bean by the name of corsConfigurationSource
}
}
And that's how I configure the cors functionality of spring security:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Unfortunatelly I got the error mentioned above, if you have an idea how to fix it please tale.
It seems that this problem was fixed with a DedupeResponseHeader-filter.
See https://github.com/spring-cloud/spring-cloud-gateway/pull/866

How to let the zuul proxy handle the cors?

I have several microservices behind a microservice acting as zuul proxy.
When called from outside the domain the microservices return a "403- Invalid Cors".
This is solved by adding the following to each microservice:
#Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**"); //just for the sake of explaination i'm allowing everything
}
}
Anyway this is a bad design: I have a zuul api gateway after all! This should be done by that and the microservices should actually return that error when directly called.
So, I moved that same configuration only to my zuul microservice, here it is by the way:
#SpringBootApplication
#EnableSwagger2
#EnableZuulProxy
#ComponentScan(basePackages = "my.base.package")
public class MyApiGateway extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return configureApplication(builder);
}
....
The props:
zuul:
prefix: /api
routes:
my-api:
path: /myApi/**
url: 'http: ...'
stripPrefix: true
Problem is that the microservice still returns "403 - Invalid Cors"! What am I doing wrong? I guess this happens because the zuul proxy actually does nothing except to forward the request...but how to configure it so to make the destination MS notice that the request comes from zuul, on the same network and so allowing it?
At least in the past Spring Cloud did not handle Zuul proxy via Spring MVC framework, so you cannot configure CORS for Zuul using WebMvcConfigurerAdapter, see: https://github.com/spring-cloud/spring-cloud-netflix/issues/888
You could try adding a CorsFilter bean to your Zuul API gateway, see: https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/web.html#mvc-cors-filter
Or you can add Spring Security to your API gateway which comes with CORS support already integrated, see: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors

Spring-Security OAuth WebMVC Invalid CORS request

Hopefully my final question to get all this working. Using Spring Security OAuth 2.0.8 and Spring-Web MVC 4.2.3 to expose the OAuth endpoints (the majority of the system uses RESTEasy for the REST endpoints, which has its own CORS filter).
I am trying to use the global default CORS support that is now in Web MVC 4.2.x. However, when issuing a test preflight request against the /oauth/token endpoint, I am always getting returned a 403 Invalid CORS Request response. Sample request from Fiddler is below.
OPTIONS http://localhost:8080/myapp/oauth/token HTTP/1.1
User-Agent: Fiddler
Host: localhost:8080
Origin: http://testfakeorigin.overtherainbow.com
Access-Control-Request-Method: POST
Even though this goes through and is determined to be a proper preflight request, it looks like the request fails in DefaultCorsProcessor at line 81 because the CorsConfiguration is null. Even if I explicitly add a CORS registry mapping in my WebMvcConfigurerAdapter (which shouldn't be necessary according to the docs), the config still ends up being null. Where should I look next?
Before the actual POST, you might automatically be issuing an OPTIONS request. By default, only the method that is specified in your RequestMapping is allowed. Therefore, you will have to explicitly allow the OPTIONS method for the cross origin request.
One way to do that, using the global configuration, is as follows:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "OPTIONS").allowedOrigins("http://testfakeorigin.overtherainbow.com");
}
This would enable cross origin requests for all you mapped requests using the GET, POST, and OPTIONS methods.
You can customize the CORS (Cross-Origin Resource Sharing) of entire app in your #Configuration class, that way all your controllers will be override automatically. Take a look:
#Configuration
#EnableWebSecurity( debug = true )
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/* ... configurations */
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues();
config.addAllowedMethod(HttpMethod.POST);
config.addAllowedMethod(HttpMethod.GET);
config.addAllowedMethod(HttpMethod.PUT);
config.addAllowedMethod(HttpMethod.DELETE);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
Note: you can define the methods verbs that will applied in your config
Best Regards!

Resources