Cors filter not working for spring boot - spring-boot

I am working on spring boot filters. I have registered the CORS bean as
#Bean
public FilterRegistrationBean simpleCORSFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
SimpleCORSFilter filter = new SimpleCORSFilter();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.applyPermitDefaultValues();
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Integer.MAX_VALUE);
bean.setFilter(filter);
return bean;
}
I have written a filter class for the same.
Though the class is getting instantiated but the request from UI is failing here.
#Component
public class SimpleCORSFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleCORSFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
LOGGER.info("start");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization,auth-token");
if(request.getMethod().equals(HttpMethod.OPTIONS.name())){
response.setStatus(HttpStatus.NO_CONTENT.value());
}else{
AuthenticationRequestWrapper requestAuth = new AuthenticationRequestWrapper((HttpServletRequest) request);
filterChain.doFilter(requestAuth, response);
}
LOGGER.info("end");
}
}
But still I am getting the following error
cors header ‘access-control-allow-origin’ missing

I don't think that you need bean for that. Also adding #Order(Ordered.HIGHEST_PRECEDENCE) just after #Component maybe will solve your problem. This will tell spring that this configuration should be with highest precedence. And in order to be consistent use HttpServletResponse.SC_OK instead HttpStatus.NO_CONTENT.value()

According to offcial Spring Boot documentation :
Enabling Cross Origin Requests for a RESTful Web Service
Here's an example of how to configure CORS with annotations and filters :
Enabling CORS
Controller method CORS configuration
So that the RESTful web service will include CORS access control headers in its response, you just have to add a #CrossOrigin annotation to the handler method:
src/main/java/hello/GreetingController.java
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
Global CORS configuration
As an alternative to fine-grained annotation-based configuration, you can also define some global CORS configuration as well. This is similar to using a Filter based solution, but can be declared within Spring MVC and combined with fine-grained #CrossOrigin configuration. By default all origins and GET, HEAD and POST methods are allowed.
src/main/java/hello/GreetingController.java
#GetMapping("/greeting-javaconfig")
public Greeting greetingWithJavaconfig(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
src/main/java/hello/Application.java
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}

I suggest to try using this one https://gist.github.com/keesun/2245179
Works for me, then add it to WebMvcConfigurer
public class Interceptors implements WebMvcConfigurer {
private final CorsInterceptor corsInterceptor;
#Autowired
public Interceptors(CorsInterceptor corsInterceptor) {
this.corsInterceptor = corsInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.corsInterceptor).addPathPatterns("/**");
}
}
I also added #Configuration to the CorsInterceptor class

I had the same issue. I thought that my spring cors configuration was wrong, but what I realized was that the request must fulfill some requirements:
the method must be OPTIONS
the presence of the Origin header
the presence of the Access-Control-Allow-Methods header
I found that here
Try this:
1-Just add the #CrossOrigin annotation to your controller
2-Send your request with this 2 headers using OPTIONS method:
Access-Control-Request-Method:GET
Origin:*
You should get your Access-Control-Allow-Origin:*
Juani

Related

CORS preflight did not succeed with Spring Boot and Kotlin [duplicate]

I use Spring Boot with Spring Security and Cors Support.
If I execute following code
url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
if xmlhttp.readyState is 4
console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:a'
do xmlhttp.send
I get as a result
200
If I test with wrong credentials like
url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
if xmlhttp.readyState is 4
console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:aa'
do xmlhttp.send
instead of getting 401 (that is the standard code for wrong authentication in spring security) I get
0
with following browser notification:
GET http://localhost:5000/api/token
XMLHttpRequest cannot load http://localhost:5000. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 401.
I'm developing front-end code that needs useful http status codes from server responses to handle the situation. I need something more useful than 0. Also the response body is empty. I dont know if my config is wrong, or it's a software bug and I also don't know where, if it's chromium (using arch linux) or spring security.
My Spring Config is:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
#RequestMapping("api")
public class Controller {
#RequestMapping("token")
#CrossOrigin
Map<String, String> token(HttpSession session) {
return Collections.singletonMap("token", session.getId());
}
}
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("a").password("a").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.anyRequest().authenticated()
.and().httpBasic();
}
}
If I test with curl everything works perfect, I think because no CORS support needed, but I tried to simulate the CORS with OPTION requests and the result was also ok.
$ curl -v localhost:5000/api/token -H "Authorization: Basic YTpha"
* Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTpha
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Access-Control-Allow-Origin: http://localhost:3000
< Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE
< Access-Control-Max-Age: 3600
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Origin,Accept,X-Requested- With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
< x-auth-token: 58e4cca9-7719-46c8-9180-2fc16aec8dff
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:15:44 GMT
<
* Connection #0 to host localhost left intact
{"token":"58e4cca9-7719-46c8-9180-2fc16aec8dff"}
and with wrong credentials:
$ curl -v localhost:5000/api/token -H "Authorization: Basic YTp"
* Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTp
>
< HTTP/1.1 401 Unauthorized
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< WWW-Authenticate: Basic realm="Realm"
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:16:15 GMT
<
* Connection #0 to host localhost left intact
{"timestamp":1462119375041,"status":401,"error":"Unauthorized","message":"Failed to decode basic authentication token","path":"/api/token"}
Edit:
To avoid misunderstandings. I use 1.3.3 Spring Boot.
The Blog post writes:
CORS support will be available in the upcoming Spring Boot 1.3 release, and is already available in the 1.3.0.BUILD-SNAPSHOT builds.
Using controller method CORS configuration with #CrossOrigin annotations in your Spring Boot application does not require any specific configuration.
Global CORS configuration can be defined by registering a WebMvcConfigurer bean with a customized addCorsMappings(CorsRegistry) method:
I have added following code to enable global cors support. actually I have tried this before but it the result was the same. I tried it again recently and the result is the same.
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}
The idea, that the problem comes from a redirect between the authorization process is an interesting though. how can i change the redirect to any resources to avoid this conflict?
EDIT:
I guess I am closer to a solution. I have tested with my nodejs server that supports cors without problems by adding
Access-Control-Allow-Origin: *
to all requests.
Like Stefan Isele has already mentioned it seems that spring security redirects or doesn't add the CORS header so that's why the request seems to be broken. So while spring security is checking the authentification it has to add the proper header.
Does anyone know how to do so?
EDIT:
I found a workaround, that seems to be ugly. I have started a github issue for spring boot where I describe the workaround: https://github.com/spring-projects/spring-boot/issues/5834
Spring Security can now leverage Spring MVC CORS support described in this blog post I wrote.
To make it work, you need to explicitly enable CORS support at Spring Security level as following, otherwise CORS enabled requests may be blocked by Spring Security before reaching Spring MVC.
If you are using controller level #CrossOrigin annotations, you just have to enable Spring Security CORS support and it will leverage Spring MVC configuration:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
If you prefer using CORS global configuration, you can declare a CorsConfigurationSource bean as following:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
This approach supersedes the filter-based approach previously recommended.
You can find more details in the dedicated CORS section of Spring Security documentation.
If you use JDK 8+, there is a one line lambda solution:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}
How to Solve CORS on Spring Boot 2.3+
Summary
If you are facing this CORS issue, don't worry. It's a common issue for every backend developer when they try to integrate with front-end microservices for the first-time. It's some sort of security policy that browsers are strictly applying for the safety of the users and that's why you are not facing it when you tried your API via Postman/Swagger or cURL.
Solutions
Client-Side Bypass (Dev Only)
Following solutions are just only for development purposes, you absolutely need to solve this CORS issue permanently for your production environment. You can use the following browser extensions to bypass browser policies for CORS error but don't get surprised if they didn't work properly.
CORS Unblock Firefox - Chrome
CORS Everywhere Firefox
Production Solutions
There are different ways to configure CORS policies on the application and it's completely based on your deployment architecture. For example, if your application is going to be exposed through Reverse Proxies (like Nginx), API Gateways (Kong), Service Mesh Sidecar Proxies (i.e Envoy), Kubernetes NGINX Ingress, and so forth, the Best Practice is to handle the CORS configuration on the Edge layer because sometimes they don't consider lower layers headers and they overwrite them and you will still receive CORS errors from the Browser.
I have listed useful links for the configuration of edge layers in the following
Kubernetes NGINX Ingress Controller CORS - Stackoverflow / Official User Manual
Kong API Gateway - KongHQ
Nginx - Geekflare
Apache - Geekflare
HA Proxy - Official Website
Istio - Discuss.Istio
But, If you are going to deploy and expose your APIs through SprintBoot's built-in web server, you can use the instructions in the next.
Instructions to Enable CORS Globally - Spring Boot Application
If you don't have any implementation for WebSecurityConfig, Just easily do the following steps:
Add the following dependency [spring-boot-starter-security]
to your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Create a new class in your config package that extends
WebSecurityConfig (i.e 'SecurityConfig')
Put the following codes into the created file:
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import java.util.List;
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
corsConfiguration.setAllowedOrigins(List.of("*"));
corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PUT","OPTIONS","PATCH", "DELETE"));
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setExposedHeaders(List.of("Authorization"));
// You can customize the following part based on your project, it's only a sample
http.authorizeRequests().antMatchers("/**").permitAll().anyRequest()
.authenticated().and().csrf().disable().cors().configurationSource(request -> corsConfiguration);
}
}
Now you need to customize the CORS configuration based on your need:
setAllowedHeaders-> you have to specify which parameters are allowed to be sent to the backend services through the front-end app, for example, if you are using Bearer/Basic Token Authorization methods, you need to pass your JWT-Token through the "Authorization" header. So you need to make sure that backed would accept this data accordingly and for this purpose, you must put "Authorization" in the list of Allowed-Headers.
setAllowedMethods-> Do not forget to put "OPTIONS" method in the list for Pre-flight process. Don't worry, read more here!
setAllowCredentials-> If you are using Authorization header, set it True.
setExposedHeaders-> If you are returning data through Response Headers, you need to specify them here. for example, some APIs are designed to return Authorization token after success /authentication through Response Headers. Thus, the related header needs to be exposed accordingly.
setAllowedOrigins-> You must specify the domains that are eligible to send requests to your backend applications. for example, if your application is hosted on https://penguin.com and your APIs are on https://api.penguin.com, you need to allow "https://penguing.com" to send requests to your backend. Also, you are able to pass wildcard (*) to allow any domains to send requests to your backend. But it's recommended to not use "any" unless you are providing public APIs or you are deploying in the non-production environments.
There is an important misunderstanding for the people that may think CORS can avoid misuses of the APIs by/on other platforms (i.e phishing purposes). It's not true, CORS Policies are browser-based policies and can be bypassed easily through proxies, so it only makes the misuse process a little bit harder, but it does not make immunity.
Build/Run your application, Test your APIs, and rest ( Everyone knows CORS headache )
Alternative Solutions
You can use the following links:
Spring.io | Enabling Cross-Origin Requests for a RESTful Web Service
Bealdung | CORS with Spring
If you are using Spring Security, you can do the following to ensure that CORS requests are handled first:
#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;
}
}
See Spring 4.2.x CORS for more information.
Without Spring Security this will work:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
}
};
}
Cross origin protection is a feature of the browser. Curl does not care for CORS, as you presumed.
That explains why your curls are successful, while the browser requests are not.
If you send the browser request with the wrong credentials, spring will try to forward the client to a login page.
This response (off the login page) does not contain the header 'Access-Control-Allow-Origin' and the browser reacts as you describe.
You must make spring to include the haeder for this login response, and may be for other response, like error pages etc.
This can be done like this :
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
}
This is copied from cors-support-in-spring-framework
I would start by adding cors mapping for all resources with :
registry.addMapping("/**")
and also allowing all methods headers..
Once it works you may start to reduce that again to the needed minimum.
Please note, that the CORS configuration changes with Release 4.2.
If this does not solve your issues, post the response you get from the failed ajax request.
// CorsConfig.java file
#Configuration
public class CorsConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowCredentials(true);
}
}
As of 2021, this is maybe the simplest solution, just need to create a separate class.
That's all.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
};
}
Found an easy solution for Spring-Boot, Spring-Security and Java-based config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().configurationSource(new CorsConfigurationSource() {
#Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return new CorsConfiguration().applyPermitDefaultValues();
}
});
}
}
I had the same problem on a methood that returns the status of the server.
The application is deployed on multiple servers. So the easiest I found is to add
#CrossOrigin(origins = "*")
#RequestMapping(value="/schedulerActive")
public String isSchedulerActive(){
//code goes here
}
This method is not secure but you can add allowCredentials for that.
I solved this problem by:
`
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers","Access-Control-Allow-Origin","Access-Control-Request-Method", "Access-Control-Request-Headers","Origin","Cache-Control", "Content-Type", "Authorization"));
configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
`
I solved this problem by:
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
#Configuration
public class CORSFilter extends CorsFilter {
public CORSFilter(CorsConfigurationSource source) {
super((CorsConfigurationSource) source);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Headers",
"Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
if (response.getHeader("Access-Control-Allow-Origin") == null)
response.addHeader("Access-Control-Allow-Origin", "*");
filterChain.doFilter(request, response);
}
}
and:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
#Configuration
public class RestConfig {
#Bean
public CORSFilter corsFilter() {
CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:4200");
config.addAllowedMethod(HttpMethod.DELETE);
config.addAllowedMethod(HttpMethod.GET);
config.addAllowedMethod(HttpMethod.OPTIONS);
config.addAllowedMethod(HttpMethod.PUT);
config.addAllowedMethod(HttpMethod.POST);
((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config);
return new CORSFilter(source);
}
}
Cors can be a pain in the ass, but with this simple code you are Cors ONLY!!!! to to specified method
#CrossOrigin(origins="*")// in this line add your url and thats is all for spring boot side
#GetMapping("/some")
public String index() {
return "pawned cors!!!!";
}
Like a charm in spring boot 2.0.2
I was having major problems with Axios, Spring Boot and Spring Security with authentication.
Please note the version of Spring Boot and the Spring Security you are using matters.
Spring Boot: 1.5.10
Spring: 4.3.14
Spring Security 4.2.4
To resolve this issue using Annotation Based Java Configuration I created the following class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("youruser").password("yourpassword")
.authorities("ROLE_USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().
authorizeRequests()
.requestMatchers(CorsUtils:: isPreFlightRequest).permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.realmName("Biometrix");
http.csrf().disable();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization"));
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
One of the major gotchas with Axios is that when your API requires authentication it sends an Authorization header with the OPTIONS request. If you do not include Authorization in the allowed headers configuration setting our OPTIONS request (aka PreFlight request) will fail and Axios will report an error.
As you can see with a couple of simple and properly placed settings CORS configuration with SpringBoot is pretty easy.
You can finish this with only a Single Class, Just add this on your class path.
This one is enough for Spring Boot, Spring Security, nothing else.
:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class MyCorsFilterConfig implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, enctype");
response.setHeader("Access-Control-Max-Age", "3600");
if (HttpMethod.OPTIONS.name().equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig config) throws ServletException {
}
}
After much searching for the error coming from javascript CORS, the only elegant solution I found for this case was configuring the cors of Spring's own class org.springframework.web.cors.CorsConfiguration.CorsConfiguration()
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}
// https://docs.spring.io/spring-boot/docs/2.4.2/reference/htmlsingle/#boot-features-cors
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*").allowedHeaders("*");
}
};
}
}
If using Spring Security, set additional:
// https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/#cors
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
// if Spring MVC is on classpath and no CorsConfigurationSource is provided,
// Spring Security will use CORS configuration provided to Spring MVC
http.cors(Customizer.withDefaults());
}
}
For properties configuration
# ENDPOINTS CORS CONFIGURATION (EndpointCorsProperties)
endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported.
endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.
endpoints.cors.allowed-methods=GET # Comma-separated list of methods to allow. '*' allows all methods.
endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled.
endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response.
endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients.
Kotlin solution
...
http.cors().configurationSource {
CorsConfiguration().applyPermitDefaultValues()
}
...
Solution for Webflux (Reactive) Spring Boot, since Google shows this as one of the top results when searching with 'Reactive' for this same problem. Using Spring boot version 2.2.2
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.cors().and().build();
}
#Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.applyPermitDefaultValues();
config.addAllowedHeader("Authorization");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
For a full example, with the setup that works with a custom authentication manager (in my case JWT authentication). See here https://gist.github.com/FiredLight/d973968cbd837048987ab2385ba6b38f
Note that new CorsConfiguration().applyPermitDefaultValues() only allows GET, POST and HEAD methods. If you are looking for PUT or DELETE, you have to set the methods manually (like below). Finally, don't forget the OPTIONS method which is required to preflight PUT, PATCH and DELETE methods (CORS error will still occur otherwise).
import static org.springframework.web.cors.CorsConfiguration.ALL;
#Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowedMethods(Arrays.asList(
HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.DELETE.name(),
HttpMethod.PUT.name(),
HttpMethod.HEAD.name(),
HttpMethod.POST.name(),
HttpMethod.OPTIONS.name()
));
config.setAllowedHeaders(Collections.singletonList(ALL));
config.setAllowedOrigins(Collections.singletonList(ALL));
config.setMaxAge(1800L);
source.registerCorsConfiguration("/**", config);
return source;
}
In class implement WebMvcConfigurer you have to Override method addCorsMappings
public class WebMvcConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS;
#Value("${appConfig.cors.allowedOrigins}")
private String[] allowedOrigins;
public WebMvcConfig() {
MAX_AGE_SECS = 3600;
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(allowedOrigins)
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(MAX_AGE_SECS);
}
}
You need to look at especially this method : configure( HttpSecurity httpSecurity )
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*")
.allowedMethods("HEAD", "GET", "PUT", "POST",
"DELETE", "PATCH").allowedHeaders("*");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/images/**").permitAll()
.antMatchers(HttpMethod.POST,"/api/login").permitAll()
.antMatchers(HttpMethod.POST,"/api/user").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // Add our custom JWT security filter
UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().configurationSource(request -> {
var cors = new CorsConfiguration();
cors.setAllowedOrigins(List.of("http://localhost:4200", "http://127.0.0.1:80", "http://example.com"));
cors.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
cors.setAllowedHeaders(List.of("*"));
return cors;
}).and()...
}
}

Spring Boot #EnableResourceServer unexpected 401 Unauthorized error [duplicate]

I have created two web applications - client and service apps.The interaction between client and service apps goes fine when they are deployed in same Tomcat instance.
But when the apps are deployed into seperate Tomcat instances (different machines), I get the below error when request to sent service app.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401
My Client application uses JQuery, HTML5 and Bootstrap.
AJAX call is made to service as shown below:
var auth = "Basic " + btoa({usname} + ":" + {password});
var service_url = {serviceAppDomainName}/services;
if($("#registrationForm").valid()){
var formData = JSON.stringify(getFormData(registrationForm));
$.ajax({
url: service_url+action,
dataType: 'json',
async: false,
type: 'POST',
headers:{
"Authorization":auth
},
contentType: 'application/json',
data: formData,
success: function(data){
//success code
},
error: function( jqXhr, textStatus, errorThrown ){
alert( errorThrown );
});
}
My service application uses Spring MVC, Spring Data JPA and Spring Security.
I have included CorsConfiguration class as shown below:
CORSConfig.java:
#Configuration
#EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
SecurityConfig.java:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("authenticationService")
private UserDetailsService userDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
}
Spring Security dependencies:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
I am using Apache Tomcat server for deployment.
CORS' preflight request uses HTTP OPTIONS without credentials, see Cross-Origin Resource Sharing:
Otherwise, make a preflight request. Fetch the request URL from origin source origin using referrer source as override referrer source with the manual redirect flag and the block cookies flag set, using the method OPTIONS, and with the following additional constraints:
Include an Access-Control-Request-Method header with as header field value the request method (even when that is a simple method).
If author request headers is not empty include an Access-Control-Request-Headers header with as header field value a comma-separated list of the header field names from author request headers in lexicographical order, each converted to ASCII lowercase (even when one or more are a simple header).
Exclude the author request headers.
Exclude user credentials.
Exclude the request entity body.
You have to allow anonymous access for HTTP OPTIONS.
Spring Security 3
Your modified (and simplified) code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
}
You still need your CORS configuration (probably with some additional values):
#Configuration
#EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
Spring Security 4
Since Spring Security 4.2.0 you can use the built-in support, see Spring Security Reference:
19. CORS
Spring Framework provides first class support for CORS. CORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request will determine the user is not authenticated (since there are no cookies in the request) and reject it.
The easiest way to ensure that CORS is handled first is to use the CorsFilter. Users can integrate the CorsFilter with Spring Security by providing a CorsConfigurationSource using the following:
#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;
}
}
Spring Security 5/6
For Spring Security 5/6 see Spring Security Reference:
CORS
Spring Framework provides first class support for CORS. CORS must be processed before Spring Security, because the pre-flight request does not contain any cookies (that is, the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request determines that the user is not authenticated (since there are no cookies in the request) and rejects it.
The easiest way to ensure that CORS is handled first is to use the CorsFilter. Users can integrate the CorsFilter with Spring Security by providing a CorsConfigurationSource that uses the following:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults())
...
return http.build();
}
#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;
}
}
With Spring Security 5/6 you don't need to configure CORS twice (Spring Security and Spring MVC):
If you use Spring MVC’s CORS support, you can omit specifying the CorsConfigurationSource and Spring Security uses the CORS configuration provided to Spring MVC:
Since Spring Security 4.1, this is the proper way to make Spring Security support CORS (also needed in Spring Boot 1.4/1.5):
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
}
and:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable();
http.cors();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Do not do any of below, which are the wrong way to attempt solving the problem:
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
web.ignoring().antMatchers(HttpMethod.OPTIONS);
Reference: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
In my case, I have Resource Server with OAuth security enabled and any of above solutions didn't work. After some debugging and googling figured why.
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
Basically in this example Ordered.HIGHEST_PRECEDENCE is key!
https://github.com/spring-projects/spring-security-oauth/issues/938
Various pom dependencies add different kinds of filters and therefore we could have issues based on order.
Add the below configuration in the main application. It worked me in spring boot application 2.3.1
package com.example.restservicecors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication
public class RestServiceCorsApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceCorsApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
};
}
}
Reference source: https://spring.io/guides/gs/rest-service-cors/
Since none of this posted examples helped me, I've taken things in my own knowledge.
In this method:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
CorsConfiguration by default have allowed method: POST, HEAD, GET, so PUT, DELETE will not work. What I did is I created a new instance of CorsConfiguration and set allowed methods:
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
so now my method looks like:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", cors.applyPermitDefaultValues());
return source;
}
Of course, all other configuration is made by Spring documentation.
Try this:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
import java.util.List;
#Component
public class CorsFilterConfig {
public static final List<String> allowedOrigins = Arrays.asList("*");
#Bean
public FilterRegistrationBean<CorsFilter> initCorsFilter() {
// #formatter:off
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
config.addAllowedMethod("*");
config.setAllowedOrigins(allowedOrigins);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
// #formatter:on
}
}
You can easily add the #CrossOrigin annotation to allow all of them if you use UsernamePasswordAuthenticationFilter. And in the security configurations the http.cors().and(). This worked for me.
#CrossOrigin(origins = "*")
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManagerBean());
customAuthenticationFilter.setFilterProcessesUrl("/api/login");
http
.csrf().disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// We can ant match out paths to the corresponding roles --> we allow certain roles to access certain API's
http
.cors()
.and();
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**").permitAll();
...
This worked for: spring-boot-starter-parent 2.2.6.RELEASE
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
}
Change "*" to something meaningful in prod

How do I enable CORS headers in the Swagger /v2/api-docs offered by Springfox Swagger?

I have the following file in my project:
#Configuration
#Order(Ordered.LOWEST_PRECEDENCE)
public class SwaggerConfig {
#Bean
public Docket apiSwagger2Documentation() { .... }
}
And in the Application.java there is:
#SpringBootApplication
#ComponentScan(basePackages = { ... })
#EnableSwagger2
public class Application {
...
}
The Swagger JSON is available under /v2/api-docs, that works fine.
What I would like to do, is to enable CORS headers for that endpoint.
For my own controllers, I have added #CrossOrigin to the controller classes, those APIs then have CORS headers, that works fine. But for the Swagger JSON URL I haven't written a controller myself, so I cannot use that annotation.
I have added the following method to the SwaggerConfig, as described in "Global CORS Configuration" in CORS support in Spring Framework.
#Bean
public WebMvcConfigurer corsConfigurer() {
System.out.println("*** corsConfigurer called");
return new WebMvcConfigurerAdapter() {
#Override public void addCorsMappings(CorsRegistry registry) {
System.out.println("*** addCorsMappings called");
registry.addMapping("/v2/api-docs");
}
};
}
Both print statements get printed, so the method is being called. But when I call the URL with curl:
curl -H "Origin: foo.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
--verbose \
http://localhost:9274/v2/api-docs
The CORS headers are not in the response. (In contrast to my own controller methods, annotated with #CrossOrigin, where the response does have the CORS headers.)
I am using springfox-swagger2 version 2.7.0, and spring-boot-starter-web 1.5.2.
What can I do to enable CORS headers on the Swagger JSON API endpoint?
I think you need a generic web filter as opposed to Web Mvc configuration.
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// Allow anyone and anything access. Probably ok for Swagger spec
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/v2/api-docs", config);
return new CorsFilter(source);
}
Thanks to #Barath for the answer. The solution was to ignore the Spring documentation, that code just seems to silently not work.
(It's a shame, the Spring stuff is quite advanced when it does work, for example, the "Access-Control-Allow-Headers" response header to the pre-flight request is set based on what headers the Java API method actually offers.)
Ignore Spring's implementation of CORS and do your own. I have put the code here that worked for me:
#Component
public class CorsFilter implements Filter {
#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", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Foo, Bar, Baz");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
}
Remember to add any #RequestHeader that you've used in any REST method to the Access-Control-Allow-Headers response header

Spring security, cors error when enable Oauth2

I'm getting an error while querying my oauth/token endpoint.
I've configured cors enable for my resource / also tried to allow all resources but nothing worked.
XMLHttpRequest cannot load http://localhost:8080/oauth/token. Response
to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:1111' is therefore not allowed
access. The response had HTTP status code 401.
vendor.js:1837 ERROR SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse (<anonymous>)
at CatchSubscriber.selector (app.js:7000)
at CatchSubscriber.error (vendor.js:36672)
at MapSubscriber.Subscriber._error (vendor.js:282)
at MapSubscriber.Subscriber.error (vendor.js:256)
at XMLHttpRequest.onError (vendor.js:25571)
at ZoneDelegate.invokeTask (polyfills.js:15307)
at Object.onInvokeTask (vendor.js:4893)
at ZoneDelegate.invokeTask (polyfills.js:15306)
at Zone.runTask (polyfills.js:15074)
defaultErrorLogger # vendor.js:1837
ErrorHandler.handleError # vendor.js:1897
next # vendor.js:5531
schedulerFn # vendor.js:4604
SafeSubscriber.__tryOrUnsub # vendor.js:392
SafeSubscriber.next # vendor.js:339
Subscriber._next # vendor.js:279
Subscriber.next # vendor.js:243
Subject.next # vendor.js:14989
EventEmitter.emit # vendor.js:4590
NgZone.triggerError # vendor.js:4962
onHandleError # vendor.js:4923
ZoneDelegate.handleError # polyfills.js:15278
Zone.runTask # polyfills.js:15077
ZoneTask.invoke # polyfills.js:15369
With Postman everything works perfect.
My cors security configuration:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*")
.allowCredentials(true);
}
}
also tried to add http://localhost:1111 in allowed origins
Code in Postman:
require 'uri'
require 'net/http'
url = URI("http://localhost:8080/oauth/token")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/x-www-form-urlencoded'
request["authorization"] = 'Basic Y2hhdHRpbzpzZWNyZXRzZWNyZXQ='
request["cache-control"] = 'no-cache'
request["postman-token"] = 'daf213da-e231-a074-02dc-795a149a3bb2'
request.body = "grant_type=password&username=yevhen%40gmail.com&password=qwerty"
response = http.request(request)
puts response.read_body
After a lot of struggling i've overrided method configure(WebSecurity web) of class WebSecurityConfigurerAdapter because Authorization server configures this by itself and i just haven't found another solution. Also you need to permitAll "/oauth/token" Http.Options method. My method:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/oauth/token");
}
After this we need to add cors filter to set Http status to OK. And we can now intecept Http.Options method.
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
#WebFilter("/*")
public class CorsFilter implements Filter {
public CorsFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig config) throws ServletException {
}
}
I found a way to fix the 401 error on Spring Security 5 and Spring Security OAuth 2.3.5 without turning off security for all OPTIONS requests on the token endpoint.
I realized that you can add a security filter to the token endpoint via the AuthorizationServerSecurityConfigurer. I tried adding a CorsFilter and it worked. The only problem I have with this method is I couldn't leverage Spring MVC's CorsRegistry. If anyone can figure out how to use the CorsRegistry, let me know.
I've copied a sample configuration for my solution below:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
#Configuration
#EnableAuthorizationServer
public static class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
//... other config
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
//... other config
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.applyPermitDefaultValues();
// Maybe there's a way to use config from AuthorizationServerEndpointsConfigurer endpoints?
source.registerCorsConfiguration("/oauth/token", config);
CorsFilter filter = new CorsFilter(source);
security.addTokenEndpointAuthenticationFilter(filter);
}
}
This worked for me
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception
{
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.applyPermitDefaultValues();
// add allow-origin to the headers
config.addAllowedHeader("access-control-allow-origin");
source.registerCorsConfiguration("/oauth/token", config);
CorsFilter filter = new CorsFilter(source);
security.addTokenEndpointAuthenticationFilter(filter);
}
}
You could extend the AuthorizationServerSecurityConfiguration and override the void configure(HttpSecurity http) method to implement a custom cors configuration while leaving the rest untouched.
Here's an example:
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration;
import org.springframework.web.cors.CorsConfiguration;
public class MyAuthorizationServerSecurityConfiguration extends AuthorizationServerSecurityConfiguration {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(request -> {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedMethod("POST");
configuration.addAllowedHeader("Content-Type");
return configuration;
}));
}
}
And then, instead of using the default annotation #EnableAuthorizationServer which pulls in the default configuration class you can import the relevant classes on your own:
#Import({AuthorizationServerEndpointsConfiguration.class, MyAuthorizationServerSecurityConfiguration.class})
No need to alter any security configuration related to OPTIONS method and/or specific oauth paths.
I had CORS errors using XMLHttpRequest to send POST /logout requests (Keycloak and Spring Cloud OidcClientInitiatedServerLogoutSuccessHandler), so I used HTML form instead:
<form action="/logout" method="post">
<button>Logout</button>
</form>
it works without any issues and no CORS config is needed.

How to handle CORS URLs on Prod/Dev environments?

In our Spring Boot app, we made the first deployment on our Quality environment and now we want to make it simple defining URLs to accept petitions from our FrontEnd application.
We build our application with maven and then we execute it with the command
java -Dspring.profiles.active=prod -jar myapp-0.0.1-SNAPSHOT.jar
We thought we could set the URL on the application.properties/application-prod.properties file, but this does not work as in execution time it is null. Another workaround would be somehow to get the parameter -Dspring.profiles.active=prod we pass when running the application and then take one URL or another but this seems to be a little dirty...
So what do you guys would do? I was impressed not finding anything on google, apparently people have different workarounds or I am searching in the wrong way.
Edit
Cross Origin info:
This is how we implemented it at first.
#CrossOrigin(origins = BasicConfiguration.CLIENT_URL)
And this is how we want to do it now with a filter with Spring Security
public class CorsFilter implements Filter, ApplicationContextAware {
#Value("${urlServer}")
private String urlServer;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", urlServer);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Expose-Headers", "Location");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
}
Of course urlServer is defined in application.properties with its corresponding metadata.
EDIT 2
How I initialize the filter:
#Bean
public FilterRegistrationBean corsFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CorsFilter());
registration.addUrlPatterns("/sessionLogin");
return registration;
}
The problem is that you CorsFilter is not a spring bean. You can eather define it like a bean, or do something like this:
#Bean
public FilterRegistrationBean corsFilter(#Value("${app.cors.url.server}") String urlServer) {
FilterRegistrationBean registration = new FilterRegistrationBean();
CorsFilter corsFilter = new CorsFilter();
corsFilter.setUrlServer(urlServer);
registration.setFilter(corsFilter);
registration.addUrlPatterns("/sessionLogin");
return registration;
}
Of course, you will need to define setter in your CorsFilter for urlServer field

Resources