I'm lost with CORS again :(
I followed docs and set up my WebConfig like this:
#EnableWebSecurity
class WebSecurityConfig() {
#Bean
fun corsConfigurationSource(): CorsConfigurationSource = CorsConfiguration()
.apply { allowedOrigins = listOf("http://localhost:3000", "*") }
.apply { allowedMethods = listOf("*") }
.let { corsConfig ->
UrlBasedCorsConfigurationSource().apply { registerCorsConfiguration("/**", corsConfig) }
}
#Bean
fun configure(http: HttpSecurity): SecurityFilterChain {
http
.csrf().disable()
.cors { }
http.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
http
.authorizeRequests()
.antMatchers(
"/auth/**",
"**/demo",
).permitAll()
.anyRequest().authenticated()
http.exceptionHandling { it.authenticationEntryPoint { _, response, authException ->
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.message)
} }
http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter::class.java)
return http.build()
}
}
and I still get CORS error, "http://localhost:3000" being blocked. I tried multiple variations, followed multiple tutorials and I just can't solve this one.
Did I make and error that I can't see?
Edit: When I "solved" this I encountered another irrational problem. Apparently the core problem was in editor/browser. After restart everything works as expected.
Guys, if you think something should work and it doesn't, just try to restart editor and browser. It may save you 2 days.
I'm going to add that CorsConfigurationSource can be replaced by CorsFilter bean.
fun corsFilter() : CorsFilter
fun corsConfigurationSource() : CorsConfigurationSource
It's really strange but after adding allowedHeaders to CorsConfiguration it works fine:
CorsConfiguration()
.apply {
allowedOrigins = listOf("http://localhost:3000")
allowedMethods = listOf("*")
allowedHeaders = listOf("*")
}
I know it's weird because spring doesn't mention allowedHeaders in their docs, but I verified this solution both on my localhost and AWS deploy and in both cases I get cors error if I omit allowedHeaders.
Very good tip is also to check Disable cache checkbox in your DevTools -> Network tab when testing your cors implementation.
To make it complete, cors can be configured by implementing of one of two beans:
fun corsFilter() : CorsFilter
fun corsConfigurationSource() : CorsConfigurationSource
Related
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.
I cannot seem to get CORS working right in Spring Boot's Webflux - here is my config and no matter what I do I get CORS errors with a VUE client:
#Configuration
#EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
#Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration()
configuration.allowedOrigins = listOf("http://localhost:8080")
configuration.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", configuration)
return source
}
#Bean
fun userDetailsService(): MapReactiveUserDetailsService {
val user: UserDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(user)
}
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http
.authorizeExchange { exchanges: AuthorizeExchangeSpec ->
exchanges
.anyExchange().authenticated()
}
.httpBasic(withDefaults())
.formLogin(withDefaults())
.csrf().disable()
.cors().configurationSource(corsConfigurationSource())
return http.build()
}
}
I've tried cors().configurationSource(withDefaults()) too (which should use the configuration source bean I've defined, according to the docs.
What do I need to do to make this work?
EDIT: Here's my browser error:
Access to XMLHttpRequest at 'http://localhost:8088/data/configuration' 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.
So, it turns out that I needed to add:
configuration.allowedHeaders = listOf("*")
Anybody that's having problems with this can add this to application.properties to see the exact reason that the request is rejected (or set your debugger to debug in the DefaultCorsProcessor class) and watch what happens:
logging.level.org.springframework.web.cors.reactive.DefaultCorsProcessor=debug
... o.s.w.c.reactive.DefaultCorsProcessor : Reject: headers '[authorization]' are not allowed
In Rest controller you could do this:
#RestController
#CrossOrigin(origins = "*")
for webflux look at this:
Enable CORS in Spring 5 Webflux?
I'm trying to solve a CORS issue with spring data rest but seems like the CORS headers are not attached. This is the config I have:
#Component
class DataRestConfig: RepositoryRestConfigurer {
override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration?, cors: CorsRegistry?) {
cors?.addMapping("/*")
?.allowedOrigins("*")
?.allowedMethods("GET", "PUT", "DELETE","PATCH","POST","OPTIONS")
}
}
I also had the same issue with other API routes that are out of spring data rest. Here is my WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
open class WebSecurityConfig(private val userDetailsServices: DatabaseUserDetailsServices, private val jwtService: JWTService): WebSecurityConfigurerAdapter() {
#Value("\${auth.jwt.secret}")
private var secret: String = ""
override fun configure(http: HttpSecurity) {
http
.cors().and()
.csrf().disable()
.addFilterAfter(JWTAuthorizationFilter(userDetailsServices, secret, jwtService),UsernamePasswordAuthenticationFilter::class.java)
.authorizeRequests()
.antMatchers(HttpMethod.POST,UserController.LOGIN_URL).permitAll()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
}
}
Edit:
Added the full WebSecurityConfigurerAdapter
I noticed that the OPTIONS request gets 403 this is why I've added the antMatchers for OPTIONS method but it did not help.
Here are the response and request headers. There is no response body:
If using Spring MVC you should configure the CORS behavior like so
#Configuration
public class CorsConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS");
}
}
I don't know why the other configs are not taken into account and I don't know if this is considered a good solution but since I only need this on the local environment it is not that important. This is how I got this working:
#Bean
#Profile("local")
open fun corsConfigurationSource(): CorsConfigurationSource{
val cors = UrlBasedCorsConfigurationSource()
val config = CorsConfiguration().applyPermitDefaultValues()
config.addAllowedMethod(HttpMethod.OPTIONS)
config.addAllowedMethod(HttpMethod.POST)
config.addAllowedMethod(HttpMethod.PATCH)
config.addAllowedMethod(HttpMethod.DELETE)
cors.registerCorsConfiguration("/**", config)
return cors
}
You can always have a CorsFilter to modify response headers. Here I have answered how we can have custom CorsFilter in Spring boot - https://stackoverflow.com/a/66882700/3709922. Kindly have a look.
Is there anyway to make this end point allow request from anywhere?
I've tried like but none of them worked.
#CrossOrigin(origins = "")
#CrossOrigin(origins = "http://")
#CrossOrigin(origins = "http://localhost:3001")
#GetMapping(path="/transactions")
public #ResponseBody List<RealEstateTransaction> getTransactions() {
return realEstateTransactionService.findTargets();
}
While working with cross domains, most of the time we tend to worry about what & where it went wrong. There are many factors including security, web components, sockets, etc to be handled at the server side before a request is processed. Many ways to implement the CORS in the Spring Boot application.
1. Annotation
By implementing #CrossOrigin like what you did in the Main class. Also can be done by adding #CrossOrigin to specific controllers/methods, if particular API should be accessed only from specific domain.
#CrossOrigin("*") // to allow from all domains
#CrossOrigin("http://localhost:3001") // to allow from specific domain
#CrossOrigin(origins = "http://localhost:3001")
2. WebConfig
If Spring Application is MVC where the resources could be accessed. Simply add the CORS mappings by overriding WebMvcConfigurer's addCorsMappings function.
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*");
}
}
3. SecurityConfig
When security is enabled in the application then CORS must be implementated in the SecurityConfig. Registering the CORS filter can be done in many ways. One is adding UrlBasedCorsConfigurationSource to the http.cors() function. Another is to create CustomCorsFilter by extending the CorsFilter.
public class CustomCorsFilter extends CorsFilter {
public CustomCorsFilter() {
super(configurationSource());
}
public static UrlBasedCorsConfigurationSource configurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOrigin("*");
configuration.addAllowedHeader("*");
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**", configuration);
return corsConfigurationSource;
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
String[] paths = {"/auth/**", "/env"};
//http.cors().configurationSource(CustomCorsFilter.configurationSource()); // Option 1
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers(paths)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.addFilterBefore(new CustomCorsFilter(), UsernamePasswordAuthenticationFilter.class); //option 2
}
I develop an angular app with a spring webflux backend. Up so far, the CorsFilter worked fine and allowed requests from the frontend.
Then I added a SecurityConfig. Since then the CorsFilter stopped working and I get an exception in the angular app:
Access to XMLHttpRequest at 'http://localhost:8080/users/999/folders/%2F/media/' from origin 'http://localhost:4200' 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
This filter worked fine:
#Configuration
public class CorsFilter {
private static final String FRONTEND_LOCALHOST = "http://localhost:4200";
private static final String FRONTEND_STAGING = "https://somehost.github.io";
#Bean
CorsWebFilter corsWebFilter() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.applyPermitDefaultValues();
corsConfig.addAllowedMethod(HttpMethod.PUT);
corsConfig.addAllowedMethod(HttpMethod.DELETE);
corsConfig.setAllowedOrigins(Arrays.asList(FRONTEND_LOCALHOST, FRONTEND_STAGING));
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return new CorsWebFilter(source);
}
}
Then I added authorization (bearer token) with following SecurityConfig:
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfiguration {
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.cors().and().csrf()
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
It seems with the security config my CorsFilter is not taken into account anymore. I red that the corsfilter needs to be added explicity in the config, but the examples I found didnt work. I hope somebody can help and knows why.
EDIT: To address the duplication concerns: I already tried adding cors() and cors().configurationSource(corsConfig()) into my security config, but didnt helped either.
I found a working way in Enable CORS in Spring 5 Webflux? by implementing a custom corsfilter.
However I still wasnt happy with the solution as it looked quite like a workaround.
Finally I found the culprit(s)... one is quite embarassing. My posted SecurityConfig was in the wrong package (it was in the default package by accident) and for this reason the config didnt picked up.
Also the cors filter from the question started working when I pasted the code to the securityconfig.
So my final SecurityConfig looks like this:
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfiguration {
private static final String FRONTEND_LOCALHOST = "http://localhost:4200";
private static final String FRONTEND_STAGING = "https://somehost.github.io";
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.csrf()
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
#Bean
CorsConfigurationSource corsConfiguration() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.applyPermitDefaultValues();
corsConfig.addAllowedMethod(HttpMethod.PUT);
corsConfig.addAllowedMethod(HttpMethod.DELETE);
corsConfig.setAllowedOrigins(Arrays.asList(FRONTEND_LOCALHOST, FRONTEND_STAGING));
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return source;
}
}
try add this config
corsConfig.setAllowCredentials(true);