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

SpringBoot+Angular跨域异常排查:已配置@CrossOrigin仍报CORS错误

问题:SpringBoot CORS配置失效排查

环境与配置

后端(Java SpringBoot)

控制器代码

@CrossOrigin(origins = "*", maxAge = 3600)//allow cross-origin resource sharing (CORS) request
@RestController
@RequestMapping("/api/school")
public class CourseController {
  @PostMapping("/login")
  public ResponseEntity<?> loginPage(@RequestBody LoginRequest loginRequest){
        *****
        Authentication authentication= authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(name, passowrd));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        UserDetails userDetails = (UserDetails)authentication.getPrincipal();
        LoginResponse loginResponse =new LoginResponse();
        loginResponse.setUsername(userDetails.getUsername());
        return ResponseEntity.ok(loginResponse);
  }
    @GetMapping("/apartment/{apartmentName}")
    public ResponseEntity<?> findTeachers(@PathVariable("apartmentName")String apartName){
        ******
     return ResponseEntity.ok(list);
        
    }
}

WebSecurity配置

@Configuration
@EnableWebSecurity //it's allow the static resource, image, css, js..
@EnableGlobalMethodSecurity(prePostEnabled = true) //allow the multiple endpoint
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors()
        .and().csrf().disable()
         .authorizeHttpRequests()
        .antMatchers("/api/school/login").permitAll() // login do not need to authenticated
        .anyRequest().authenticated()
        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().httpBasic();
        
    }
}

前端(Angular)

服务代码

export class CourseService {
  private baseUrl = 'http://localhost:9010/api/school';
  constructor(private _httpClient: HttpClient) {}

  login(request: LoginRequest): Observable<LoginResponse> {
    return this._httpClient
      .post<LoginResponse>(this.baseUrl + '/login', request)
      .pipe(catchError(this.errorHandler));
  }
  getAllTeachers(apartmentName: string): Observable<TeachersResponse[]> {
    return this._httpClient
      .get<TeachersResponse[]>(this.baseUrl + '/apartment/' + apartmentName)
      .pipe(catchError(this.errorHandler));
  }
}

组件调用代码

loadData(apartmentName: string) {
    console.log('call courseService');
    this._courseService.getAllTeachers(apartmentName).subscribe((teachers) => {
      this.teacherArray = teachers;
      console.log(teachers);
    });
  }

问题描述

登录接口可正常访问,跳转至课程页面后,调用loadData方法获取教师列表时触发CORS错误:

origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

已尝试以下配置但均无效:

  • 控制器类上添加@CrossOrigin(origins = "*", maxAge = 3600)或指定具体 origin
  • 给控制器方法单独添加@CrossOrigin
  • WebSecurity配置中开启http.cors()
    最终在SecurityConfig类中添加自定义CorsConfigurationSource方法后问题解决,想知道原有配置失效的原因。

原因分析

  1. Spring Security的CORS优先级高于控制器注解
    当你在WebSecurity中调用http.cors()时,Spring Security会启用自己的CORS过滤器,这个过滤器会优先处理跨域请求,直接忽略控制器上的@CrossOrigin注解。而默认情况下,Spring Security的CORS配置非常严格,几乎不允许任何跨域请求,所以受保护接口的跨域验证直接失败。

  2. 登录接口正常的特殊原因
    登录接口/api/school/login被设置为permitAll(),意味着该请求会绕过Spring Security的认证过滤器,同时也会跳过Spring Security的CORS过滤器。此时控制器上的@CrossOrigin注解才会生效,所以登录请求没有出现CORS问题。而获取教师列表的接口需要认证,请求必须经过Spring Security的CORS过滤器,因无有效配置导致跨域失败。

  3. 自定义CorsConfigurationSource生效的逻辑
    当你定义了CorsConfigurationSource Bean后,Spring Security的CORS过滤器会直接使用这个配置处理跨域请求,覆盖默认的严格规则,从而允许你的前端域名访问受保护接口。

补充方案

如果不想自定义CorsConfigurationSource,也可以通过以下方式让控制器注解生效,但不推荐这种方式(会增加受保护接口的安全风险):
在WebSecurity配置中禁用Spring Security的CORS过滤器:

http.cors().disable()

这种方式仅适合所有接口都通过控制器注解配置CORS的场景,无法统一管理跨域规则。更推荐的做法是通过CorsConfigurationSource配置全局CORS规则,既能配合Spring Security的认证机制,又能统一管控跨域策略。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:36:21