Issue with authentication and cross-origin resource sharing (CORS) when implementing Spring Boot, Spring Security, Vue.js,

Running on vue.js 3, Vite 4.0.2, axios 0.25.0, and spring boot (Starter 2.7.2).

A backend has been created in spring boot while using vue.js3, vite, and axios for the UI. Now, I simply want to make a call to rest with axios. Before implementing these functions, I tested the REST API with Postman and IntelliJ HTTP request, all successful.

The following setup is in place: Spring boot app: http://localhost:9050 Vue.js3: (where x can be 1-4).

To get this working, I checked various answers here on stackoverflow, but none provided a solution, despite extensive research.

Below is the code snippet:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig implements WebMvcConfigurer {

    // Code continues...

On the other side, in Vue. Note: * added as allow-origin just to make it work, planning a more restrictive setting later.

module.exports = {
    devServer: {
        proxy: {
            '^/api/v1/products/all': {
                target: 'http://localhost:9050/',
                ws: true,
                changeOrigin: true,
                withCredentials: true,
                // Code continues...
            },
        }
    }
}

Lastly, a script in Vue:

const { data, status } = await axios.get<GetProductPojoResponse>(
        'http://localhost:9050/api/v1/products/all',
        {
          headers: {
            Accept: 'application/json',
            // Code continues...
          },
        },
    );

    console.log(JSON.stringify(data, null, 4));

    console.log('response status is: ', status);

    return data;

Encountering CORS errors along with error 401 and 403, struggling to find an explanation...

Answer №1

When dealing with CORS errors, it's often an issue originating from the backend. It appears that you may not be clearly defining which links are allowed access to your backend. In your filterChain() method within the corsConfiguration, consider adding something like corsConfiguration.setAllowedOrigins(List.of("vueJsSiteLinkHereGoesHere"));

This is the security configuration I utilized for my Angular project. Keep in mind that I did not implement WebMvcConfigurer.

Additionally, ensure that you do not have the @CrossOrigins annotation on your controller class as it could override the settings specified here.

I am using JWT for authentication, so you may need to adjust headers according to your requirements.

package com.example.backend.security;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;

@EnableWebSecurity
@EnableMethodSecurity()
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
    private final AppProperties appProps;
    private static final String[] WHITELISTED_URLS = {
            "/auth/**",
    };

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors(Customizer.withDefaults()) // by default, use a bean of the name corsConfigurationSource
                .csrf(AbstractHttpConfigurer::disable) // not really needed in REST
                .authorizeHttpRequests((requests) -> requests
                        .requestMatchers(new AntPathRequestMatcher("/h2/**")).permitAll()
                        .requestMatchers(WHITELISTED_URLS).permitAll()
                        .anyRequest().authenticated()
				)
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .sessionManagement((session) -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                );
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of(appProps.getFrontendSite()));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("Authorization", "Access-Control-Allow-Origin", "Content-Type",
                "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"));
        configuration.setAllowCredentials(false); // we're using jwt
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

For further information, check out this video by Dan Vega on YouTube. He also covers some related content on Vue: https://youtu.be/HRwlT_etr60

   

Answer №2

There are several approaches to solving this issue:

First, in the Backend code, you can set the allowed origin patterns to List.of("*").

However, this is not the most ideal solution.

Another workaround involves copying the distribution files into the target folder and configuring a forwarding to index.html.

It seems crazy that making REST requests in a non-browser environment always succeeds without any cross-site errors...

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Converting HTML elements into Vue.js Components

In my Vue.js application, I am utilizing a d3.js plugin to generate a intricate visualization. I am interested in implementing a customized vue directive to the components that were incorporated by the d3 plugin. It seems that the $compile feature, which ...

Troubleshooting Tips for Resolving Problems with VueJS getElementById Bug

I'm currently working with a VueJS single File component that has the following template: <template> <div class="row"> <div class="col-md-12"> <div id="hottable"></div> < ...

Does Vuejs have a counterpart to LINQ?

As a newcomer to javascript, I am wondering if Vue has an equivalent to LinQ. My objective is to perform the following operation: this.selection = this.clientsComplete.Where( c => c.id == eventArgs.sender.id); This action would be on a collect ...

Information displays instantly in the initial milliseconds

When developing dynamic web pages with Nuxt, I encountered an issue in the pages directory where a file named _url.vue is located. The contents of this file are as follows: <template lang="pug"> div component( v-for= ...

Vue project encountering issue with displayed image being bound

I am facing an issue with a component that is supposed to display an image: <template> <div> <img :src="image" /> </div> </template> <script> export default { name: 'MyComponent', ...

The dropdown in vue-multiselect automatically closes after the first selection is made, ensuring a smooth user experience. However,

I am experiencing an issue where the dropdown closes after the first selection, despite setting close-on-select="false". However, it works properly after the initial select. You can observe this behavior directly on the homepage at the following link: vue ...

Errors in vue.js conditions

I am currently attempting to validate whether my variable is empty. Despite reviewing my code, I am facing issues with its functionality. My current version of vue.js is 2.5.13 Below you can find the snippet of my code: <template> <div v-if ...

What are the best practices for utilizing bootstrap-vue's panel component effectively?

Transitioning my project from vue-strap to bootstrap-vue has hit a snag for me. I'm having difficulty migrating the panel. Here's the current vue-strap code snippet: <div class="col-sm-3"> <panel is-open type="info"> < ...

Creating a bar chart in Chart JS using an array of data objects

I need to create a unique visualization using a bar chart where each bar represents a user or student. Each bar will have an xAxis label displaying the student's name. The code below is a VueJS computed property named chartData For my bar chart data ...

What's the most effective method to incorporate additional events into this element using the conditional operator?

Looking for help with this code snippet: <span role="link" tabindex="0" :class="tabDetails.showPayment ? 'link' : ''" @click="tabDetails.showPayment ? cTab('payments') : null" ...

Error in vue.js: Unable to call this.$emit as it is not a function

export default { mounted() { setTimeout(function() { this.$emit('onLoad') }, 4000); } } //views/Load.vue I want to redirect to another page after the page has been accessed for 4 seconds. <template> <d ...

The functionality of the Vueify modal seems to be malfunctioning when incorporating Vueify alongside a central Modal.vue file that houses modal templates

I've been experimenting with integrating this tutorial on creating reusable modal dialogs with Vuejs and adapting it for use with Vueify. Initially, I was able to successfully implement it, but after exploring a different approach briefly, I returned ...

The Vue.js error message "Unable to access property 'array_name' as it is undefined" indicates an issue with

I'm currently working on fetching data using Axios requests and storing it in an array. Below is the code I have been using: props: [ 'products', ], data: function () { return { algolia: '', pro ...

Clicking on a Vuetify v-btn with the :href attribute set to download will open the XML file

I'm having trouble getting the v-btn to download an XML file instead of opening it in the browser. <v-btn :disabled="!exportUrl" block x-large height="100" color="primary" :href="exportUrl" download> ...

Ways to remove items from Vuex store by utilizing a dynamic path as payload for a mutation

I am looking to implement a mutation in Vuex that dynamically updates the state by specifying a path to the object from which I want to remove an element, along with the key of the element. Triggering the action deleteOption(path, key) { this.$store.d ...

Express JS causing NodeJS error | "Issue with setting headers: Unable to set headers after they have been sent to the client"

As I embark on my journey to learn the fundamentals of API development, I am following a tutorial on YouTube by Ania Kubow. The tutorial utilizes three JavaScript libraries: ExpressJS, Cheerio, and Axios. While I have been able to grasp the concepts being ...

Ways to transmit information among Vue components

I am trying to figure out how to pass data from the Module variable in CoreMods.vue to ExternalWebpage.vue using Vuex. I want the CoreModule state to be watched for changes in the external webpage. This is my store.js setup: import Vue from 'vue ...

Guide on utilizing map function in JavaScript and Vue to generate a fresh array

I am working on implementing a map method in JavaScript and Vue to generate a new array of objects while only including specific items in each object. I have designed a user interface with 2 checkboxes corresponding to 2 distinct objects: <div v-for ...

What seems to be the issue with the data initialization function not functioning properly within this Vue component?

In my Vue 3 component, the script code is as follows: <script> /* eslint-disable */ export default { name: "BarExample", data: dataInitialisation, methods: { updateChart, } }; function dataInitialisation() { return { c ...

Is the popup not opening with just one click?

https://i.stack.imgur.com/09dcf.png Upon clicking the first input cell, the popup opens and closes as expected. However, when closing the initial input and opening another one, an orange mark icon appears but the popup doesn't open until the second c ...