Open In App

Implementing CORS in Spring Boot with Spring Security

Last Updated : 16 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

CORS issue is one of the common issues being faced in web development. We are here going to configure CORS in our backend application built using Spring Boot and Spring security, being used for security purpose.

Before going to fix the issue, lets understand what CORS is and how it works and why browsers show error while sending CORS request.

What is CORS?

Cross-Origin Resource Sharing (CORS) is a type of additional security layer that you can consider which is implemented by most of the browsers. It is the specification provided by W3C, which all browsers implement by default. This CORS issue arises when you make request to server from different origin. The difference can be between port numbers or domain or the protocols (http/https).

Actions performed by browsers while making a CORS request

  • Browsers will first make a preflight request to the server checking for the requested domain is allowed to access the resource or not. Preflight request is made by browsers automatically in case of CORS request before original request is sent to server.
  • After making pre-flight request and getting response from the server, it will decide whether to make the request to the server or not. And if not, it will throw a CORS error otherwise it will make the actual request.
  • Browsers will not make the pre-flight request each time to the server, it caches the response of first pre-flight response and will use the same response.
  • The cache cleans up according to what maxAge you have specified in the CORS configs in server side. After that tenure, if any further request is requested to make, then it will make the pre-flight call again and this process will continue.

Configuring CORS in Spring Boot application with the help of Spring Security

We are going to create our spring application and will configure according to our needs. We will be having two applications; one will be named as server and other as client both running on different ports.

We will see how CORS configuration makes the communication possible between these two applications running on different ports or different origins.

Prerequisites:

  • JDK 17 or above
  • IntelliJ Idea or your other favourite IDE


Now, we will create one server application and one client application.

Server Application

Step 1: Creating Server application

Create a spring boot application from Spring Initializer with these set of configurations provided below.

Add two dependencies by clicking on add dependencies button:

  • Spring Web
  • Spring Security
Project Metadata
Server application

Note: Here, we are going to use the latest spring security version 6.X, just mentioning that spring security 5 or below were earlier involving WebSecurityConfigurerAdapter class, where we used to provide the spring security related configs. But in spring security 6, those things have been deprecated and removed by introducing SecurityFilterChain in latest version, where you have to provide the configurations related to spring security.

pom.xml file:

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>Server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Server</name>
    <description>Server</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


Step 2: Create Controller

We will have a GET API exposed in this controller with endpoint as "/hello". It will return a string message "Hello World" in response.

HelloController.java:

Java
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class HelloController {

    @GetMapping("/hello")
    public ResponseEntity<String> hello() {
        return ResponseEntity.ok("Hello World");
    }
}


Step 3: Setting Server property in application.properties file

spring.application.name=Server
server.port=5050

We will run our server on port 5050.

Step 4: Create Configuration Class

Create a SecurityConfiguration class and add the following code. This file will have the configurations related to CORS.

In this file, we have configured the bean of SecurityFilterChain. This bean is responsible for all the spring security related configurations. In this bean only, we are providing configs related to CORS in cors() method of HttpSecurity.

SecurityConfiguration.java:

Java
import jakarta.servlet.http.HttpServletRequest;
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.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;

import static org.springframework.security.config.Customizer.withDefaults;


@Configuration
public class SecurityConfiguration {



    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.cors(corsCustomizer->corsCustomizer.configurationSource(new CorsConfigurationSource() {
                    @Override
                    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                        CorsConfiguration corsConfiguration=new CorsConfiguration();
                        corsConfiguration.setAllowCredentials(true);// allows taking authentication with credentials
                        corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:7070"));
                      // providing the allowed origin details, can provide multiple origins here, 7070 is the port number of client application here
                        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));// allowing all HTTP methods GET,POST,PUT etc, can configure on your need
                        corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));// allowing all the request headers, can configure according to your need, which headers to allow
                        corsConfiguration.setMaxAge(Duration.ofMinutes(5L)); // setting the max time till which the allowed origin will not make a pre-flight request again to check if the CORS is allowed on not
                        return corsConfiguration;
                    }
                }))
                .authorizeHttpRequests(auth->auth.requestMatchers("/hello").permitAll()//this autorizeHttpRequests method is related to configuring your API security based on roles, this is not realted to CORS
                        .anyRequest() .authenticated())
                        .formLogin(withDefaults());

        return httpSecurity.build();

    }
}

Note: This was just the configuration related to CORS, you can modify as per your need.

Output:

Server Console


Client Application

Similar to server app, we will create another spring boot application which is named as "Client". It will run on different port and will make call to "/hello" endpoint of server application.

Step 1: Create Client Application

Select the configurations in spring initializer for client application.

Add two dependencies by clicking on add dependencies button:

  • Spring Web
  • Spring Reactive Web

Here, we are using spring reactive web to use WebClient to make REST API call to server application.

Project Metadata
Client Application


pom.xml file:

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>Client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Client</name>
    <description>Client</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Step 2: WebClient Bean Configuration in ClientApplication.java

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.client.WebClient;

import java.beans.BeanProperty;

@SpringBootApplication
public class ClientApplication {

    @Bean
    public WebClient webClient(){
        return WebClient.builder().baseUrl("http://localhost:5050").build();
    }


    public static void main(String[] args) {

        SpringApplication.run(ClientApplication.class, args);
    }

}

Step 3: Creating HelloController.java in Client application

This controller will have an endpoint "/hello" and when we make a call to "/hello" endpoint of client application, then it will internally make a call to "/hello" endpoint of server application.

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;

@RestController
public class HelloController {

    @Autowired
    private WebClient webClient;

    @GetMapping("/hello")
    public ResponseEntity<String> hello(){
        return webClient.get().uri("/hello").retrieve()
                .toEntity(String.class).block();
    }
}


Step 4: Adding properties in application.properties file

spring.application.name=Client
server.port=7070

Client application will run on 7070 port number.

Output:

We will now start application and hit the "/hello" endpoint of the application.

Client Console


Hitting endpoint "/hello" of server:

Getting expected response from server application


Now we will try to make request to "/hello" endpoint of client application which will internally make call to server application to its "/hello" endpoint. We are configured the CORS properly in server side so will get proper response in client application.

Output after proper CORS configuration:

After CORS configuration


We have seen how to configure CORS in our application and tested the output through client application by making a CORS request. You can control the settings of CORS as per your requirements.



Next Article

Similar Reads