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

Angular使用Basic Authorization获取token时预检请求返回401错误

解决Angular中Basic Authorization请求access_token时的预检401错误

嘿,这个问题我之前也碰到过!核心原因是浏览器的跨域预检机制在搞事情:当你发送带自定义头(比如Authorization)的跨域请求时,浏览器会自动先发一个OPTIONS类型的预检请求,确认服务器是否允许这个跨域操作。如果服务器对OPTIONS请求也要求Basic Auth验证,就会返回401,浏览器直接就把后续的POST请求拦下来了——而Postman不会自动发预检请求,所以能正常拿到token。

下面我帮你调整代码,同时告诉你服务器端必须做的配置,双管齐下解决问题:

一、优化Angular请求代码

你的代码有几个可以规范的地方,调整后更符合OAuth2的请求规范:

  1. HttpHeaders是不可变对象,直接链式创建更简洁
  2. 把grant_type、username、password放到请求体里(而不是URL参数),这是OAuth2密码模式的标准做法
  3. 确保请求体是application/x-www-form-urlencoded格式的字符串

修改后的服务代码:

requestToken() {
  const authUsername = 'my-username';
  const authPassword = 'my-password';
  // 编码Basic Auth凭证
  const encodedCredentials = btoa(`${authUsername}:${authPassword}`);

  // 构建请求体,转为x-www-form-urlencoded格式
  const requestBody = new URLSearchParams();
  requestBody.set('grant_type', 'password');
  requestBody.set('username', authUsername);
  requestBody.set('password', authPassword);

  // 链式创建请求头
  const requestHeaders = new HttpHeaders()
    .set('Content-Type', 'application/x-www-form-urlencoded')
    .set('Authorization', `Basic ${encodedCredentials}`);

  return this._http.post(
    `${API_URL}/oauth/token`,
    requestBody.toString(), // 转成符合格式的字符串
    { headers: requestHeaders, withCredentials: true }
  );
}

组件调用可以保留,建议加上详细错误打印方便调试:

this._dataService.requestToken()
  .subscribe(
    res => {
      sessionStorage.setItem('access_token', res['access_token']);
      sessionStorage.setItem('refresh_token', res['refresh_token']);
    },
    err => {
      console.error('Authentication failed! 详细错误:', err); // 打印完整错误信息
    }
  );

二、服务器端关键配置(重中之重!)

这一步是解决预检401的核心,你必须让服务器对OPTIONS请求跳过Basic Auth验证,同时返回正确的CORS响应头:

如果后端是Spring Boot(常见的OAuth2后端)

可以在Security配置里放行OPTIONS请求,同时配置CORS:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 先放行OPTIONS预检请求,不做Auth验证
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
            .anyRequest().authenticated()
            .and()
            // 开启Basic Auth
            .httpBasic();
    }

    // 配置CORS允许跨域请求
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowCredentials(true);
        // 替换成你的前端域名,比如http://localhost:4200
        corsConfig.addAllowedOrigin("http://localhost:4200");
        corsConfig.addAllowedHeader("*");
        corsConfig.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", corsConfig);
        return new CorsFilter(source);
    }
}

如果用Nginx做反向代理

在Nginx配置里添加对OPTIONS请求的特殊处理:

location /oauth/token {
    # 处理OPTIONS预检请求
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin "你的前端域名";
        add_header Access-Control-Allow-Methods "POST, OPTIONS";
        add_header Access-Control-Allow-Headers "Authorization, Content-Type";
        add_header Access-Control-Allow-Credentials "true";
        return 204; # 返回204空响应即可,无需内容
    }
    # 其他代理配置
    proxy_pass http://你的后端服务地址;
}

做完这两步,浏览器的预检OPTIONS请求就能被服务器正常响应(返回204),不会触发401,后续的POST请求就能顺利拿到access_token啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:27:39