开发者

SpringBoot+Vue跨域配置(CORS)问题得解决过程

开发者 https://www.devze.com 2024-08-14 10:34 出处:网络 作者: 繁依Fanyi
目录1. 问题描述2. 解决方案概述3. 试错过程3.1 初步尝试:简单的 CORS 配置3.2 细化 Security 配置3.3 尝试代理配置3.4 最终解决方案:完善的 CORS 和 Security 配置4. 为什么要这样修改5. 结果1. 问题描述
目录
  • 1. 问题描述
  • 2. 解决方案概述
  • 3. 试错过程
    • 3.1 初步尝试:简单的 CORS 配置
    • 3.2 细化 Security 配置
    • 3.3 尝试代理配置
    • 3.4 最终解决方案:完善的 CORS 和 Security 配置
  • 4. 为什么要这样修改
    • 5. 结果

      1. 问题描述

      在我们开发的过程中,vue 前端需要与 Spring Boot 后端通信。如果后端没有正确配置 CORS,浏览器会进行跨域检查并阻止请求,报错信息如下:

      Access to XMLHttpRequest at 'http://localhost:8789/auth/register' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
      

      2. 解决方案概述

      为了解决这个问题,我们需要在 Spring Boot 应用中配置 CORS。这个过程包括创建一个 CORS 配置类,并在 Spring Security 配置类中应用这个配置。

      3. 试错过程

      3.1 初步尝试:简单的 CORS 配置

      我首先尝试在 Spring Boot 中添加一个简单的 CORS 配置类:

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.cors.CorsConfiguration;
      import org.springframework.web.cors.CorsConfigurationSource;
      import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
      
      @Configuration
      public class CorsConfig {
      
          @Bean
          public CorsConfigurationSource corsConfigurationSource() {
              CorsConfiguration configuration = new CorsConfiguration();
              configuration.addAllowedOrigin("*");
              configuration.addAllowedMethod("*");
              configuration.addAllowedHeader("*");
              configuration.setAllowCredentials(true);
              configuration.setMaxAge(3600L);
      
              UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
              source.registerCorsConfiguration("/**", configuration);
              return source;
          }
      }
      

      然后,在 WebSecurityConfig 中应用这个配置:

      http.cors().configurationSource(corsConfigurationSource());
      

      结果,前端依旧报错,没有任何变化。

      3.2 细化 Security 配置

      我接着尝试在 WebSecurityConfig 中进一步细化 CORS 配置:

      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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
      import org.springframework.web.cors.CorsConfiguration;
      import org.springframework.web.cors.CorsConfigurationSource;
      import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
      
      @Configuration
      @EnableWebSecurity
      public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.cors().configurationSource(corsConfigurationSource())
                  .and().csrf().disable();
          }
      
          @Bean
          CorsConfigurationSource corsConfigurationSource() {
              CorsConfiguration configuration = new CorsConfiguration();
              configuration.addAllowedOrigin("*");
              configuration.addAllowedMethod("*");
              configuration.ad编程dAllowedHeader("*");
              configuration.setAllowCredentials(true);
              configuration.setMaxAge(3600L);
      
              UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
              source.registerCorsConfiguration("/**", configuration);
              return source;
          }
      }
      

      然而,前端还是无法正常发起跨域请求,这让我非常困惑。

      3.3 尝试代理配置

      为了确保开发过程中跨域请求能正确代理到后端,我在 Vue 项目中添加了代理配置:

      首先,确保项目使用 vue-cli 创建,并确保有 vue.config.js 文件。然后添加如下代理配置:

      let proxyObj = {};
      proxyObj['/'] = {
          target: 'http://localhost:8789/',
          changeOrigin: true,
          pathRewrite: {
              '^/': ''
          }
      }
      
      module.exports = {
          devServer: {
              open: true,
              host: 'localhost',
              port: 8081,
              proxy: proxyObj,
          },
      }
      

      这种配置可以使前端的跨域请求通过代理转发到后端。不过,这只是开发环境下的解决方案,并没有真正解决后端的 CORS 配置问题。

      3.4 最终解决方案:完善的 CORS 和 Security 配置

      经过几次尝试和查阅资料后,我最终找到了一个有效的解决方案,结合之前的经验,创建了一个完善的 CORS 和 Security 配置。

      CorsConfig.Java

      package cn.techfanyi.fanyi.config;
      
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.cors.CorsConfiguration;
      import org.springframework.web.cors.CorsConfigurationSource;
      import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
      
      @Configuration
      public class CorsConfig {
      
          @Bean
          public CorsConfigurationSource corsConfigurationSource() {
              CorsConfiguration corsConfig = new CorsConfiguration();
              corsConfig.addAllowedOriginPattern("*"); // 允许任何源
              corsConfig.addAllowedMethod("*"); // 允许任何HTTP方法
              corsConfig.addAllowedHeader("*"); // 允许任何HTTP头
              corsConfig.setAllowCredentials(true); // 允许证书(cookies)
              corsConfig.setMaxAge(3600L); // 预检请求的缓存时间(秒)
      
       tFwIVF       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
              source.registerCorsConfiguration("/**", corsConfig); // 对所有路径应用这个配置
              return source;
          }
      }
      

      WebSecurityConfig.java

      package cn.techfanyi.fanyi.config;
      
      import cn.techfanyi.fanyi.filter.JwtRequestFilter;
      import cn.techfanyi.fanyi.security.CustoMACcessDeniedHandler;
      import cn.techfanyi.fanyi.security.CustomAuthenticationEntryPoint;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.authentication.AuthenticationManager;
      import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
      import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
      import org.springframework.security.config.annotation.web.builders.HttpSecurity;
      import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
      import org.springframework.security.config.http.SessionCreationPolicy;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      import org.springframework.security.web.SecurityFilterChain;
      import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
      
      @Configuration
      @EnableWebSecurity
      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class WebSecurityConfig {
      
          private final JwtRequestFilterjs jwtRequestFilter;
          private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
          private final CustomAccessDeniedHandler customAccessDeniedHandler;
      
          public WebSecurityConfig(JwtRequestFilter jwtRequestFilter,
                                CustomAuthenticationEntryPoint customAuthenticationEntryPoint,
                                CustomAccessDeniedHandler customAccessDeniedHandler) {
              this.jwtRequestFilter = jwtRequestFilter;
              this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
              this.customAccessDeniedHandler = customAccessDeniedHandler;
          }
      
          @Bean
          public SecurityFilterChain securityFilterChain(HttpSecurity http) throws  Exception {
              http.csrf().disable()
                      .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                      .authorizeRequests(authorizedRequests ->
                              authorizedRequests.requestMatchers("/**").permitAll()
                                      .anyRequest().authenticated())
                      .exceptionHandling(exceptionHandling ->
                              exceptionHandling.authenticationEntryPoint(customAuthenthttp://www.devze.comicationEntryPoint)
                                      .accessDeniedHandler(customAccessDeniedHandler))
                      .sessionManagement(sessionManagement ->
                              sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                      .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
      
              return http.build();
          }
      
          @Bean
          public BCryptPasswordEncoder passwordEncoder() {
              return new BCryptPasswordEncoder();
          }
      
          @Bean
          public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
              return authenticationConfiguration.getAuthenticationManager();
          }
      
          private CorsConfigurationSource corsConfigurationSource() {
              return new CorsConfig().corsConfigurationSource();
          }
      }
      

      但是php又出现以下错误:

      java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
      

      这个错误信息表明,在 Spring Boot 的 CORS 配置中,当 allowCredentials 设置为 true 时,allowedOrigins 不能包含特殊值 "*", 因为浏览器不允许在 Access-Control-Allow-Origin 响应头中设置 "*", 同时还允许凭证(如 cookies)。此时应该使用 allowedOriginPatterns 来代替 allowedOrigins。

      具体的错误原因如下:

      java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
      

      这意味着当 allowCredentials 设置为 true 时,不能将 allowedOrigins 设置为 "*", 因为它不能在响应头中设置 Access-Control-Allow-Origin 为 "*", 同时还允许凭证。为了解决这个问题,您需要将 allowedOrigins 改为使用 allowedOriginPatterns。

      修改 CorsConfigurationSource 如下:

       @Bean
       public CorsConfigurationSource corsConfigurationSource() {
           CorsConfiguration corsConfig = new CorsConfiguration();
           corsConfig.addAllowedOriginPattern("*"); // 使用 allowedOriginPatterns 代替 allowedOrigins
           corsConfig.addAllowedMethod("*"); 
           corsConfig.addAllowedHeader("*");
           corsConfig.setAllowCredentials(true);
           corsConfig.setMaxAge(3600L);
      
           UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
           source.registerCorsConfiguration("/**", corsConfig);
           return source;
       }
      

      通过以上配置,可以解决 allowCredentials 和 allowedOrigins 中 "*" 冲突的问题,使得您的 Spring Boot 应用可以正确处理跨域请求。

      通过以上配置,前端请求终于可以成功与后端通信,CORS 问题不再出现。

      4. 为什么要这样修改

      在 Spring Security 6 中,安全配置的方式有所变化。与之前版本相比,Spring Security 6 更加灵活和模块化。为了使 CORS 配置生效,我们需要:

      1. 明确指定 CORS 配置源:在 securityFilterChain 方法中,通过 http.cors(cors -> cors.configurationSource(corsConfigurationSource())) 明确指定使用我们自定义的 CorsConfigurationSource
      2. 禁用默认的 CSRF 保护:对于大多数 API 项目,特别是无状态的 RESTful 服务,禁用 CSRF 是常见的做法。通过 http.csrf().disable() 来实现。
      3. 配置异常处理和会话管理:确保我们的应用是无状态的,并且正确处理认证和授权异常。

      5. 结果

      经过这些配置,前端可以顺利地与后端通信,避免了 CORS 错误。整个过程让我对 CORS 配置有了更深入的理解。

      以上就是SpringBoot+Vue跨域配置(CORS)问题得解决过程的详细内容,更多关于SpringBoot Vue跨域配置解决的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号