即使 withCredentials 为真,Angular 也不会发送在 Set-Cookie 中收到的 Cookie

标签 angular

对于基于 cookie 的身份验证,我的服务器将 Set-Cookie 发送到我的 Angular 应用程序。但是,应用程序不会在进一步的请求中发回该值。以下是我的代码。

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
  withCredentials: true //this is required so that Angular returns the Cookies received from the server. The server sends cookies in Set-Cookie header. Without this, Angular will ignore the Set-Cookie header
};

public getUserProfile(){
    console.log('contacting server at '+this.API_URL +this.GET_USER_PROFILE_URL+"with httpOptions "+httpOptions);
    return this.http.get(this.GET_USER_PROFILE_URL,httpOptions )
      .map(response=>{
        console.log('response from backend service',response);
        let result= <ServerResponse>response; 
        console.log("result is "+result.result+' with additional information '+result.additionalInformation)
        return result;
      })
      .catch(this.handleError);
  }

服务器在我代码的200OK中如下发送cookie(此处未显示)

设置 Cookie:id=...

然而,下一条消息在 cookie 中没有得到 id,因此服务器返回 401。如果我使用浏览器的调试工具手动添加 Cookie,那么我得到 200OK。因此,我确定是导致问题的 cookie 中缺少 id 值。

我做错了什么?我是否需要显式存储在 Set-Cookie 中收到的 cookie 并在进一步的请求中显式添加它?

更新 - 在 SPA 初始加载时,服务器发送 Set-Cookie header 以及一些与 CSRF 相关的其他 cookie 信息。我注意到该 cookie 仍由应用程序发送。会不会是 Angular 尊重第一个 Set-Cookie header 但忽略后续 header ?

我添加了几张图片来解释我的意思

在签名过程中,客户端发送一个与CSRF相关的cookie。我不认为这是必需的,因为客户端也发送 CSRF header ,但出于某种原因它确实如此。服务器响应带有 id 的 Set-Cookie

enter image description here

然后当我请求配置文件时,客户端再次发送 CSRF cookie 而不是 id cookie

enter image description here

最佳答案

终于,我找到了问题所在。旅程比结果更令人满意,所以让我将其分解为解决问题的步骤。

总而言之,这不是 Angular 的问题。我发送的 cookie 上有 secureCookie 标记。当我在没有 https 的情况下测试我的应用程序时,似乎 Angular 应用程序没有使用(或访问)200 OK 中收到的 Set-Cookie header

我将登录请求发送到服务器并处理其响应的初始代码是

return this.http.post(this.SIGNIN_USER_URL, body, httpOptions)
  .map(response => {
    console.log('response from backend service', response);
    let result= <ServerResponse>response; 
    console.log('result is ' + result.result + ' with additional information '+result.additionalInformation)
    return result;
  })
  .catch(this.handleError);

我没有使用 observe: 'response' 选项,这意味着响应将只包含正文,而不包含标题。我将代码更改为以下内容,以便我可以看到正在接收哪些 header 。

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
     
  withCredentials: true, 
  observe: 'response' as 'response'
};  
    
public signinUser(user: UserSigninInfo): any {
  console.log('contacting server at ' + this.API_URL + this.SIGNIN_USER_URL + " with user data " + user + " with httpOptions " + httpOptions.withCredentials + "," + httpOptions.headers ); 
    
  let signinInfo = new UserSignin(user);
  let body = JSON.stringify(signinInfo);
  return this.http.post(this.SIGNIN_USER_URL, body, httpOptions).catch(this.handleError);
}

上面的代码被调用如下。我更改它以获取响应中的 header

return this.bs.signinUser(user).subscribe((res: HttpResponse<any>) => {console.log('response from server:', res);
  console.log('response headers', res.headers.keys())
});

我还创建了一个拦截器来打印传入和传出的消息(从 SO 复制)

import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/do';
    
@Injectable()
export class CustomInterceptor implements HttpInterceptor {
    
  constructor() {}
    
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    
    console.log("outgoing request",request);
    request = request.clone({ withCredentials: true });
      console.log("new outgoing request",request);
    
      return next
        .handle(request)
        .do((ev: HttpEvent<any>) => {
          console.log("got an event",ev)
          if (ev instanceof HttpResponse) {
            console.log('event of type response', ev);
          }
      });
  }
}

当我开始调试时,我注意到虽然服务器发送了 10 个 header ,但只有 9 个被打印

来自服务器的 header

enter image description here

在控制台上打印消息(Set-Cookie 丢失!我的应用程序需要获取身份验证 cookie 的 header )

0: "Content-Length"
​
1: "Content-Security-Policy"
​
2: "Content-Type"
​
3: "Date"
​
4: "Referrer-Policy"
​
5: "X-Content-Type-Options"
​
6: "X-Frame-Options"
​
7: "X-Permitted-Cross-Domain-Policies"
​
8: "X-XSS-Protection"
​
length: 9

这给了我一个方向,即应用程序没有看到 Set-Cookie header 。我以为我可以通过在播放框架 exposedHeaders = ["Set-Cookie"] 中添加 CORS 策略来解决它,但这没有用。后来我仔细观察了 cookie 并注意到 secureCookie 设置

Set-Cookie: id=...Secure; HTTPOnly

这让我觉得我的 cookie 设置可能不适合我的环境(本地主机,没有 HTTPS)。我更改了 Silhoutte 中的 cookie 设置

val config =  CookieAuthenticatorSettings(secureCookie=false)

成功了!

虽然我会让上面的代码适用于 secureCookie 而这不是 Angular 的问题,但我希望某些人可能会发现这种方法有帮助

关于即使 withCredentials 为真,Angular 也不会发送在 Set-Cookie 中收到的 Cookie,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50076352/

相关文章:

javascript - Angular2 Material 中的 Md-ripple

javascript - 在 Angular 2 的另一个服务中注入(inject)自定义服务

angular - 如何取消订阅/停止 Observable?

javascript - Angular 2 : How to keep two methods from repeating

Angular 4 HttpClientModule 和 map() 函数

带有组件的 Angular7 元素出现架构错误

Angular Testing 错误 : Can't resolve all parameters for Service:

html - Angular 9——父级内部的多个子级

javascript - 系统配置 defaultExtension 不适用于 Django

javascript - 如何配置 Angular2 Quickstart 来运行 phantomjs?