通过基本身份验证访问 REST 的 Angular2 抛出“预检响应具有无效的 HTTP 状态代码 401”

Posted

技术标签:

【中文标题】通过基本身份验证访问 REST 的 Angular2 抛出“预检响应具有无效的 HTTP 状态代码 401”【英文标题】:Angular2 accessing REST via Basic Auth throws "Response for preflight has invalid HTTP status code 401" 【发布时间】:2016-11-05 21:32:11 【问题描述】:

我们目前正在尝试在 Angular2-App 中访问 Spring Boot 应用程序的 REST-Endpoint,但尽管 Authorization 标头在 Postman 中有效(Authorization/Basic dXNlcjp1c2Vy,对于具有密码 user 的基本测试用户),当应用程序尝试访问 REST-Endpoint 时它不起作用。 Spring-Security 的配置如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter 

@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception 
    builder.inMemoryAuthentication().withUser("user").password("user").roles("USER").and().withUser("admin")
            .password("admin").roles("ADMIN");


@Override
protected void configure(HttpSecurity http) throws Exception 
    http.authorizeRequests().anyRequest().fullyAuthenticated();
    http.httpBasic();
    http.csrf().disable();

Angular 应用在​​发出第一个请求之前会要求提供凭据,如下所示:

loginTest() 
    let headers: Headers = new Headers('Content-Type': 'application/json', 'Accept': 'application/json');
    headers.append('Authorization', 'Basic ' + window.btoa(this._username + ':' + this._password));
    console.log(headers);
    return this.http.get('http://localhost:8080/', headers: headers)
        .map((res:any) => res.json);

使用 localhost:8080 作为 Spring Boot 应用程序。 我们已经尝试添加 Content-Policy 标签,因为早期项目也存在类似问题,如下所示:

<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' 'self';
connect-src 'self' http://localhost:3000/ http://localhost:8080/ ws://localhost:3000/; img-src 'self' data:;
style-src 'unsafe-inline' 'self'; font-src 'self'"/>

但这也没有用。 我们得到的错误:

XMLHttpRequest cannot load http://localhost:8080/. Response for preflight has invalid HTTP status code 401

当通过 Chrome 查看请求时,这是响应:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=4659A74A950FE6C99948D8F4CB245E27; Path=/; HttpOnly
WWW-Authenticate: Basic realm="Realm"
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: accept, authorization, content-type
Access-Control-Allow-Credentials: true
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Mon, 04 Jul 2016 07:54:30 GMT

与这里的 PostMan 相比,我看不出 Angular2-App 有什么不同。

【问题讨论】:

【参考方案1】:

我认为您的问题与 CORS 有关。 Postman 是一个 chrome 插件,因此它有权执行 CORS 请求。您的 Angular 应用程序并非如此:它受到更多限制。

看到这个问题:

how Postman send requests? ajax, same origin policy

对于 Angular,您需要处理 CORS。您有一个在发送实际请求之前发送的预检请求 (OPTIONS)。我认为在您的情况下,凭据不会发送到此预检请求中,因此服务器无法对其进行身份验证并返回 401 状态代码。这会阻止执行目标请求。

我看到了两种解决方案:

使用 withCredentials 参数为 true(在 RC2 的 Angular2 中可用)

this.http.get('http://...',  withCredentials: true )...

仅检查目标请求的安全性,而不检查预检请求的安全性。这是在服务器端进行的配置。

有关详细信息,请参阅这些文章:

http://restlet.com/blog/2015/12/15/understanding-and-using-cors/ http://restlet.com/blog/2016/09/27/how-to-fix-cors-problems/

【讨论】:

感谢四位您的帮助,这是正确的指针。我们无法将 withCredentials 设置为使用 http,但我们能够禁用 OPTIONS-requests 的授权检查,这解决了问题(给我们另一个错误消息,但这不是这张票的问题 =))

以上是关于通过基本身份验证访问 REST 的 Angular2 抛出“预检响应具有无效的 HTTP 状态代码 401”的主要内容,如果未能解决你的问题,请参考以下文章

如何从 django rest 框架访问 jwt 令牌到 Angular 前端

未提供 Angular + Django REST 身份验证凭据

Angular 2 前端 django 2 REST 框架后端用户身份验证

使用 jwt 令牌的 Django API Rest Framework 和 Angular 7 身份验证

如何通过 Wordpress REST API 对 LearnDash 中受保护数据的远程访问进行身份验证?

Angular 5 应用程序的任何组件访问数据。正在通过身份验证服务中的 API 调用设置数据