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

Angular与Spring Boot跨域CORS配置问题求助(Spring Security 5.7.3)

解决Angular与Spring Boot跨域CORS问题(带凭证请求)

问题描述

从浏览器访问REST接口http://localhost:4200/api/auth/signin时,跨域请求被CORS策略拦截,控制台报错:

Access to XMLHttpRequest at 'http://localhost:8484/api/auth/signin' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

使用Spring Security 5.7.3(已弃用WebSecurityConfigurerAdapter),Postman测试/api/auth/signin接口正常。

现有代码

WebSecurityConfig类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
  @Value("${allowed.origin}")
  private String allowedOrigin ;
  @Value("${spring.h2.console.path}")
  private String h2ConsolePath;
  
  @Autowired
  UserDetailsServiceImpl userDetailsService;

  @Autowired
  private AuthEntryPointJwt unauthorizedHandler;

  @Bean
  public AuthTokenFilter authenticationJwtTokenFilter() {
    return new AuthTokenFilter();
  }

  @Bean
  public DaoAuthenticationProvider authenticationProvider() {
      DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
       
      authProvider.setUserDetailsService(userDetailsService);
      authProvider.setPasswordEncoder(passwordEncoder());
   
      return authProvider;
  }

  @Bean
  public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
    return authConfig.getAuthenticationManager();
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }


  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
        .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
        .authorizeRequests().antMatchers("/api/auth/**").permitAll()
        .antMatchers("/api/test/**").permitAll()
        .antMatchers(h2ConsolePath + "/**").permitAll()
        .anyRequest().authenticated();

 // fix H2 database console: Refused to display ' in a frame because it set 'X-Frame-Options' to 'deny'
    http.headers().frameOptions().sameOrigin();

    http.authenticationProvider(authenticationProvider());

    http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

    return http.build();
  }
@Bean
  public WebMvcConfigurer GetCorsConfugation(){
    return  new WebMvcConfigurer() {
      @Override
      public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins(allowedOrigin)
                .allowedMethods("GET","POST","PUT","DELETE")
                .allowedHeaders("*") ;
      }
    };
}

}

AuthController类

@CrossOrigin(origins  = {"http://localhost:4200"})
@RestController
@RequestMapping("/api/auth")
public class AuthController {
  @Autowired
  AuthenticationManager authenticationManager;

  @Autowired
  UserRepository userRepository;

  @Autowired
  RoleRepository roleRepository;

  @Autowired
  PasswordEncoder encoder;

  @Autowired
  JwtUtils jwtUtils;

  @PostMapping("/signin")
  public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {

    Authentication authentication = authenticationManager
        .authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));

    SecurityContextHolder.getContext().setAuthentication(authentication);

    UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();

    ResponseCookie jwtCookie = jwtUtils.generateJwtCookie(userDetails);

    List<String> roles = userDetails.getAuthorities().stream()
        .map(item -> item.getAuthority())
        .collect(Collectors.toList());

    return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
        .body(new UserInfoResponse(userDetails.getId(),
                                   userDetails.getUsername(),
                                   userDetails.getEmail(),
                                   roles));
  }
}

解决方案

问题核心是请求携带了凭证(JWT Cookie),但CORS配置未开启allowCredentials,且Spring Security的CORS过滤器优先级高于WebMvc的配置,导致原有设置未生效。

步骤1:修改Spring Security的CORS配置

替换原有的WebMvcConfigurer CORS配置,改为在Spring Security中直接配置CorsConfigurationSource:

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    // 使用配置文件中指定的允许源,不能用*(带凭证请求不允许通配符)
    configuration.setAllowedOrigins(Collections.singletonList(allowedOrigin));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
    configuration.setAllowedHeaders(Arrays.asList("*"));
    configuration.setAllowCredentials(true); // 关键:允许携带凭证
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

然后更新SecurityFilterChain中的CORS配置:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // 替换原有的http.cors().and()为以下代码
    http.cors(cors -> cors.configurationSource(corsConfigurationSource())).and()
        .csrf().disable()
        .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
        .authorizeRequests().antMatchers("/api/auth/**").permitAll()
        .antMatchers("/api/test/**").permitAll()
        .antMatchers(h2ConsolePath + "/**").permitAll()
        .anyRequest().authenticated();

    http.headers().frameOptions().sameOrigin();

    http.authenticationProvider(authenticationProvider());

    http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

    return http.build();
}

步骤2:移除控制器上的@CrossOrigin注解

删除AuthController类上的@CrossOrigin(origins = {"http://localhost:4200"}),避免全局配置和局部配置冲突。

步骤3:配置Angular请求携带凭证

在Angular的HttpClient请求中设置withCredentials: true,确保请求携带Cookie:

this.http.post('http://localhost:8484/api/auth/signin', loginRequestData, { withCredentials: true })
  .subscribe(response => {
    // 处理登录响应
  });

原理说明

  • 当请求包含凭证(如Cookie)时,浏览器要求响应头Access-Control-Allow-Credentials必须为true,且Access-Control-Allow-Origin不能使用通配符*(必须指定具体源)。
  • Spring Security的CORS过滤器优先级高于Spring MVC的WebMvcConfigurer配置,因此直接在SecurityFilterChain中配置CORS才能确保设置生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 03:01:06