Spring-Security OAuth WebMVC Invalid CORS request - spring

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!

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.

Spring boot custom response header blocked by CORS

I have Spring Boot application that provides GET REST API endpoint to a list of objects. The list has a default pagination and provides custom 'Link' HTTP header with info about the next and previous pages based on the current page in the navigation for the client to utilize.
Example of Link HTTP header
link: <http://localhost:8080/api/v1/articles?page=1&size=1>; rel="next", <http://localhost:8080/api/v1/articles?page=4&size=1>; rel="last"'
When the client and web server utilizes the same origin then the header is included. However, I am unable to include the link header in the response header when the client has different origin. The application has CORS configuration, but I couldn't find anything to make it include my custom header. Only default response headers are included.
Any idea how include custom HTTP headers in the CORS response?
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public SpringDataUserDetailsService customUserDetailsService() {
return new SpringDataUserDetailsService();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/admin").authenticated().and().formLogin();
}
}
-- EDIT 1 --
Response headers when the client is of the same origin as web server
Response headers when the client is on the other port
I found the solution to my problem. The custom headers must be exposed in CorsConfigurationSource bean.
Adding this line of code, allowed cross orgin requests to get the custom header 'Link' in response.
configuration.addExposedHeader("Link");

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

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

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 use Spring WebSessionIdResolver with Spring Security 5 in a Spring webflux application?

Currently I was experiencing the new Spring reactive stack, and want to use reactive features in Spring Session 2.0.
In traditional Servlet approach, Spring Session provides a HttpSessionStrategy to detect session in cookie or request headers. It is easy to use HeaderHttpSessionStrategy to implement a token like authentication(by default the he name is X-AUTH-TOKEN) for RESTful APIs.
Spring 5 core provides a WebSessionIdResolver to do the same thing for Reactive environment.
But when use it with Spring Security and wish it worked as traditional way, I can not get it work.
The SessionConfig file.
#EnableSpringWebSession
public class SessionConfig {
#Bean
public ReactorSessionRepository sessionRepository() {
return new MapReactorSessionRepository(new ConcurrentHashMap<>());
}
#Bean
public WebSessionIdResolver headerWebSessionIdResolver() {
HeaderWebSessionIdResolver resolver = new HeaderWebSessionIdResolver();
resolver.setHeaderName("X-SESSION-ID");
return resolver;
}
}
The partial SecurityConfig.
#EnableWebFluxSecurity
class SecurityConfig {
#Bean
SecurityWebFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeExchange()
.pathMatchers(HttpMethod.GET, "/posts/**").permitAll()
.pathMatchers(HttpMethod.DELETE, "/posts/**").hasRole("ADMIN")
//.pathMatchers("/users/{user}/**").access(this::currentUserMatchesPath)
.anyExchange().authenticated()
.and()
.build();
}
A test rest controller file, it returns the current Session ID.
#RestController
public class SessionRestController {
#GetMapping("/sessionId")
public Map<String, String> sessionId(WebSession session){
Map<String, String> map = new HashMap<>();
map.put("id", session.getId());
return map ;
}
}
When I started up the application, and use curl to access the /sessionId, there is no session info the response header.
curl -v -u "user:password" http://localhost:8080/sessionId
And I got the session id in the query result and put it into request headers to access the protected resources and got 401.
curl -v -X POST -H "X-SESSION-ID:xxxx" http://localhost:8080/posts
Update: A working sample can be found here.
Spring Framework's spring-web module defaults to using it's CookieWebSessionIdResolver, which is based on cookies. If you create an alternative bean of type HeaderWebSessionIdResolver, it will get picked up automatically by Spring Session and switch to a header-based strategy.
In either strategy, it's geared to read the incoming ServerExchange headers, and look up the session id, whether that is reading the Cookie header or the SESSION http header.
These strategies also create response headers, whether that is a set-cookie directive for the client (web browser or your code) to populate the Cookie, or to give you the SESSION header (default name for the HeaderWebSessionIdResolver's header name).

Resources