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

Angular访问Spring Boot授权服务器持续出现CORS错误

解决Angular调用Spring Boot OAuth授权时的404与CORS问题

我来帮你一步步拆解并解决这个问题,你现在遇到了两个核心问题:OPTIONS请求返回404,以及浏览器的CORS跨域限制。咱们先从404的根源开始排查,再处理CORS的配置:

第一步:先搞定OPTIONS请求404的问题

浏览器发送OPTIONS预检请求找不到路径,说明你的Spring Boot OAuth服务器要么没正确配置/oauth/authorize端点,要么这个路径被拦截了,甚至可能你用错了请求方法。

  • 确认授权端点是否正确配置
    检查你的AuthorizationServer配置类,确保/oauth/authorize端点已经正确启用。比如:

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            // 这里要确保配置了认证管理器、令牌存储等核心组件,端点才会生效
            endpoints.authenticationManager(authenticationManager)
                    .tokenStore(tokenStore);
        }
    }
    
  • 检查Spring Security是否拦截了端点
    你的WebSecurity配置不能把/oauth/authorize给拦截了,至少要允许匿名访问(或者根据你的业务需求配置权限):

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/oauth/authorize").permitAll() // 放开授权端点的访问限制
                .anyRequest().authenticated();
        }
    }
    
  • 注意请求方法的兼容性
    划重点:/oauth/authorize端点默认是支持GET请求的,如果你用POST请求,得确认你的授权服务器是否允许POST方式的授权请求。比如授权码模式通常可以用POST,但有些配置下可能只开了GET。可以先尝试用GET请求测试这个端点是否能正常访问,排除路径本身的问题。

第二步:处理CORS跨域限制

解决了404之后,还得处理浏览器的CORS预检问题。这里要注意:Spring Security的过滤器优先级很高,普通的@CrossOrigin注解或者WebMvc的CORS配置可能不生效,必须在Spring Security层面配置CORS。

方法1:全局配置CORS(推荐)

在你的WebSecurity配置类里,直接启用CORS并配置允许的源、方法和头:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 一定要把cors()放在前面,确保CORS过滤器先执行
        http.cors().and()
            .authorizeRequests()
            .antMatchers("/oauth/authorize").permitAll()
            .anyRequest().authenticated();
    }

    // 定义CORS的具体规则
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        // 允许你的Angular应用域名
        corsConfig.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        // 允许的请求方法,必须包含OPTIONS预检请求
        corsConfig.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
        // 允许的请求头,根据你的需求调整
        corsConfig.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
        // 如果需要携带Cookie(比如会话认证),开启这个
        corsConfig.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对所有路径应用这个CORS规则,也可以只针对/oauth/**配置
        source.registerCorsConfiguration("/**", corsConfig);
        return source;
    }
}

方法2:针对OAuth端点单独配置CORS

如果全局配置不符合你的需求,也可以单独给OAuth的端点配置CORS,在AuthorizationServer配置类里添加:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients()
                // 给OAuth端点绑定CORS配置
                .corsConfigurationSource(corsConfigurationSource());
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        corsConfig.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
        corsConfig.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
        corsConfig.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/oauth/**", corsConfig);
        return source;
    }
}

第三步:Angular端的请求也要配到位

确保你的Angular请求设置了正确的请求头,并且如果需要携带凭证的话开启withCredentials:

import { HttpClient, HttpHeaders } from '@angular/common/http';

// 比如在你的授权服务中
constructor(private http: HttpClient) {}

getAuthToken() {
    // OAuth授权请求通常用application/x-www-form-urlencoded格式
    const headers = new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded'
    });
    // 构造请求参数,根据你的授权模式调整(比如授权码模式、密码模式)
    const bodyParams = new URLSearchParams();
    bodyParams.set('client_id', '你的客户端ID');
    bodyParams.set('response_type', 'code'); // 或者token,根据模式来
    bodyParams.set('redirect_uri', 'http://localhost:4200/auth-callback');
    bodyParams.set('scope', 'read write'); // 你的权限范围

    return this.http.post('http://localhost:9999/oauth/authorize', bodyParams.toString(), {
        headers: headers,
        withCredentials: true // 如果需要携带Cookie(比如用户登录会话),一定要开这个
    });
}

最后提醒一句:如果是授权码模式,其实更推荐让浏览器直接跳转到/oauth/authorize端点,让用户完成登录授权后再回调到Angular应用,而不是用AJAX请求。AJAX请求更适合密码模式、客户端凭证模式这类不需要用户交互的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:54:26