Thursday, July 13, 2023

How to Configure CORS in a Spring Boot + Spring Security application

Cross-Origin Resource Sharing (CORS) is an essential mechanism for controlling access to web resources across different domains. When developing a Spring Boot application with Spring Security, it is crucial to configure CORS properly to allow or restrict cross-origin requests based on security requirements. In this article, we will explore how to configure CORS in a Spring Boot + Spring Security application, ensuring secure and controlled access to resources. We will provide step-by-step instructions and relevant code examples to help you implement CORS effectively.

How to Configure CORS in a Spring Boot + Spring Security application

Now, let's go deeper and find out how exactly you can configure CORS or Cross-Origin  Resource Sharing in Spring Boot application using Spring Security. 

Understanding CORS

CORS is a browser-based security mechanism that enforces restrictions on cross-origin requests. By default, web browsers restrict such requests due to the same-origin policy. However, there are scenarios where cross-origin requests need to be allowed, such as when consuming APIs from different domains. CORS configuration enables the server to specify which domains are allowed to access its resources.

Configuring CORS in a Spring Boot + Spring Security application

To configure CORS in a Spring Boot + Spring Security application, we need to make modifications in both the Spring Boot configuration and the Spring Security configuration. Let's go through the steps required to set up CORS.

Step 1: Add CORS dependencies

First, ensure that you have the necessary dependencies in your Spring Boot project. Include the following dependencies in your build file (pom.xml for Maven or build.gradle for Gradle):

For Maven
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
 
For Gradle

implementation 'org.springframework.boot:spring-boot-starter-web'



Step 2: Configure CORS in Spring Boot


Next, configure CORS in the Spring Boot application by creating a configuration class.

Create a class named CorsConfig and annotate it with @Configuration and @EnableWebMvc to enable CORS configuration for Spring MVC:
@Configuration
@EnableWebMvc
public class CorsConfig {
}
 
Step 3: Define CORS rules

Inside the CorsConfig class, define the CORS rules using the addCorsMappings method:
@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
 
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}
 
In the above example, we allow cross-origin requests for URLs starting with "/api/**" from the "http://localhost:3000" domain. We also specify the allowed HTTP methods, headers, and enable support for credentials.

Step 4: Configure Spring Security to allow CORS

To ensure that Spring Security honors the CORS configuration, we need to add a filter to the security chain.

Create a class named CorsFilter and extend the OncePerRequestFilter class:


public class CorsFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
// Specify the allowed origin domains 
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
// Specify the allowed HTTP methods 
 response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); 

// Specify the allowed headers 
 response.setHeader("Access-Control-Allow-Headers", "*"); 
// Enable support for credentials (e.g., cookies)
 response.setHeader("Access-Control-Allow-Credentials", "true"); 
  filterChain.doFilter(request, response); } }  



How to Configure CORS in a Spring Boot + Spring Security application




Step 5: Register the CORS filter in the security configuration

In your security configuration class (usually extending WebSecurityConfigurerAdapter), register the CorsFilter:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Configure other security settings
        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
    }
}
 

In the above example, we add the CorsFilter before the ChannelProcessingFilter to ensure that the CORS configuration is applied to all requests.

Step 6: Testing CORS Configuration

After implementing the CORS configuration, it's important to test whether it's working as expected. You can use tools like cURL or browser developer tools to test cross-origin requests.

Example:
Suppose you have a RESTful endpoint /api/users that retrieves user data. With the CORS configuration in place, you can make a cross-origin request from a different domain, such as http://localhost:3000, using JavaScript's fetch API.

fetch('http://localhost:8080/api/users')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));




Additional Considerations for CORS Configuration

Fine-grained CORS Configuration

The examples provided in the previous steps demonstrate configuring CORS for specific origins, methods, headers, and enabling credentials. However, you can customize the CORS configuration further based on your application's requirements. For instance, you can set different CORS configurations for different endpoints or apply more granular rules based on request parameters or headers.

Dynamic CORS Configuration

In some cases, you may need to dynamically determine the allowed origins or headers based on runtime conditions. Spring Boot provides flexibility in configuring CORS dynamically. You can implement a custom CorsConfigurationSource that retrieves the CORS configuration from a database, external service, or any other source, allowing you to modify the CORS configuration at runtime.

Global CORS Configuration

The examples provided in this article demonstrate configuring CORS for specific endpoints or URL patterns. However, if you want to apply the same CORS configuration globally to all endpoints in your application, you can modify the CorsConfig class as follows:

@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}
 
In the modified configuration, the addMapping("/**") specifies that the CORS configuration applies to all endpoints in the application.

Security Considerations


While enabling cross-origin access through CORS can be beneficial, it's essential to consider security implications. Always ensure that you restrict cross-origin access to trusted domains and avoid using wildcard (*) for allowed origins unless absolutely necessary. Additionally, validate and sanitize the input received from cross-origin requests to prevent potential security vulnerabilities.


Conclusion

Configuring CORS in a Spring Boot + Spring Security application is essential for enabling controlled access to resources from different domains. By following the steps outlined in this article, you can configure CORS effectively and ensure secure cross-origin requests.

Remember to specify the allowed origins, methods, headers, and enable support for credentials based on your specific requirements. With proper CORS configuration, you can enhance the security and accessibility of your Spring Boot + Spring Security application.

In a Spring Boot + Spring Security application, configuring CORS is crucial for controlling cross-origin requests and ensuring the security of your resources. By following the steps outlined in this article, you can successfully configure CORS in your application.

Remember to define the allowed origins, methods, headers, and enable support for credentials based on your specific requirements. Regularly test your CORS configuration to ensure it functions as expected. With proper CORS configuration, you can securely and selectively allow cross-origin access to your Spring Boot + Spring Security application's resources, enhancing its flexibility and usability.





1 comment:

  1. Thanks for pointing out the different details to consider when using both Security and MVC! I would like to ask a few questions, if you would.

    Since Spring Security 5.7.0-M2 WebSecurityConfigurerAdapter was deprecated. Based on your example I'd propose a change to step 5 likeso:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.access.channel.ChannelProcessingFilter;


    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

    return http.build();
    }

    }

    Also, I'm not sure whether or not pre-flight requests are already handled by your setup, and I'm pretty sure CSRF is not disabled as per the Spring Security defaults. So if anyone would like to do so — even if just for development purposes — they'd probably have to add http.csrf(AbstractHttpConfigurer::disable); to that bean to disable CSRF and perhaps even http.cors(withDefaults()); to enable pre-flights, right?

    ReplyDelete

Feel free to comment, ask questions if you have any doubt.