You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 18 对接Spring Boot(含Spring Security)的CORS问题求助

Angular 18 + Spring Boot(集成Spring Security)CORS问题解决方案

问题说明

作为Angular 18新手,使用集成Spring Security的Spring Boot后端时遇到CORS相关问题:

  • 组件ngOnInit中发起的GET请求首次加载报错,刷新页面后错误消失
  • 发起POST请求时直接触发CORS错误

相关组件代码:

export class ClassesComponent implements OnInit {
  classList$: Observable<any[]> | undefined; // Observable for async pipe
  isModalOpen = false;
  newClass = { classname: '', facultyname: '', description: '' };

  http = inject(HttpClient);
  classesService = inject(ClassserviceService);

  constructor() {
    
  }

  ngOnInit(): void {
    // ngOnInit will run after the component is constructed
    console.log('ngOnInit working...');
    
    this.classList$ = this.classesService.getData(); // Return observable directly
   
  }

  // Method to open modal for creating a new class
  openCreateClassModal() {
    this.isModalOpen = true;
  }

  // Method to close the modal
  closeCreateClassModal() {
    this.isModalOpen = false;
    this.newClass = { classname: '', facultyname: '', description: '' }; // Reset form
  }

  // Method to create a new class
  createClass() {
    this.http.post('http://localhost:8080/api/classes', this.newClass, {
      headers: { 'Content-Type': 'application/json' }
    }).subscribe({
      next: () => {
        // Fetch the updated list of classes after creation
        this.classList$ = this.classesService.getData(); // Update classList$ with new data
        this.closeCreateClassModal(); // Close the modal after creation
      },
      error: (error) => {
        console.error('Error creating class:', error);
      }
    });
  }
}

核心解决方案:后端Spring Security CORS配置

CORS问题本质是后端未正确配置跨域权限,尤其是Spring Security会优先拦截请求,普通@CrossOrigin注解可能不生效,需做全局配置:

1. 全局CORS配置类

创建WebConfig类,实现WebMvcConfigurer:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**") // 匹配你的API路径前缀
                .allowedOrigins("http://localhost:4200") // Angular开发服务器地址
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的HTTP方法
                .allowedHeaders("*") // 允许所有请求头
                .allowCredentials(true); // 允许携带Cookie等凭证
    }
}

2. Spring Security中启用CORS

在Security配置类中,将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.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用CORS配置
                .csrf(csrf -> csrf.disable()) // 前后端分离场景可临时关闭CSRF,生产环境建议配置CSRF令牌
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/classes/**").permitAll() // 根据业务需求配置API权限
                        .anyRequest().authenticated()
                );
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("http://localhost:4200"));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }
}

前端优化建议

1. 统一使用Service处理HTTP请求

将createClass方法移至ClassserviceService,避免组件直接依赖HttpClient,保持代码解耦:

// classservice.service.ts
createClass(newClass: any): Observable<any> {
  return this.http.post('/api/classes', newClass, {
    headers: { 'Content-Type': 'application/json' }
  });
}

组件中调用:

createClass() {
  this.classesService.createClass(this.newClass).subscribe({
    next: () => {
      this.classList$ = this.classesService.getData();
      this.closeCreateClassModal();
    },
    error: (error) => console.error('Error creating class:', error)
  });
}

2. 配置Angular代理

避免硬写后端地址,创建src/proxy.conf.json:

{
  "/api": {
    "target": "http://localhost:8080",
    "secure": false,
    "changeOrigin": true
  }
}

修改angular.json的serve配置,添加代理引用:

"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "proxyConfig": "src/proxy.conf.json"
  }
}

之后请求可简写为/api/classes,无需完整域名。

问题原因说明

  • GET请求刷新后正常:浏览器缓存了首次OPTIONS预检请求的结果,后续请求直接通过
  • POST请求直接报错:POST属于非简单请求,必须先发送OPTIONS预检请求,若Spring Security未配置CORS,预检请求会被拦截,导致跨域错误

内容的提问来源于stack exchange,提问作者nithikesh bekkary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 21:57:09