【问题标题】:Angular is not sending the Cookie received in Set-Cookie even if withCredentials is true即使 withCredentials 为真,Angular 也不会发送在 Set-Cookie 中收到的 Cookie
【发布时间】:2018-10-09 02:56:43
【问题描述】:

对于基于 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如下(这里没有显示)

Set-Cookie: id=...

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

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

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

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

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

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

【问题讨论】:

    标签: angular


    【解决方案1】:

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

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

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

      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' 选项,这意味着响应将只包含正文,而不包含标题。我将代码更改为以下,以便我可以看到正在接收哪些标头。

    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);
      }
    

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

    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 个标头,但只有 9 个被打印

    来自服务器的标头

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

    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 标头。我想我可以通过在游戏框架exposedHeaders = ["Set-Cookie"] 中添加CORS 策略来解决它,但这没有用。后来我仔细看了看 cookie,发现secureCookie 设置

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

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

    val config =  CookieAuthenticatorSettings(secureCookie=false)
    

    它成功了!

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

    【讨论】:

    • 一个对我有帮助的注释 - 我误解了 options 对象并掩盖了如何在此处和其他地方添加 withCredentials。它不是标题,不应添加为标题。它是一个单独的选项,位于标题旁边,仅由 Angular 使用。它不应该被发送到后端或任何东西。
    • 对于任何寻找答案的人 - 这是正确的。对我来说,这很简单,经过漫长的几天后,添加到拦截器 - withCredentials: true
    • 如果您删除了 Secure 标志以使其在本地开发中工作,它在生产中如何工作?
    • 谢谢。我刚刚为本地开发关闭了安全标志。
    • 此解决方案帮助部分解决了我的问题,+1 用于详细说明。谢谢
    【解决方案2】:

    带有“HttpOnly”标志的“Set-Cookie”表示您无法从客户端读取 cookie。

    【讨论】:

      【解决方案3】:

      我假设您使用 nodejs 和 express-session 来管理会话,然后在 express-session 中 httpOnly 默认启用,因此您必须更改 httpOnly 以获取控制台服务器发送的 cookie

      const app = require('express')();
      var session = require('express-session');
      var cookieParser = require('cookie-parser');

      app.use(会话( { 秘密:'你好世界',
      商店:SessionStore,
      重新保存:假,
      饼干:{
      安全:假,
      httpOnly:false // 默认为布尔值 true }
      }));

      在下图中 httpOnly 是真的,所以你不能控制这个 cookie。

      在下图中 httpOnly 是 false ,所以你可以控制台这个 cookie。

      【讨论】:

        【解决方案4】:

        注意 - 如果您在项目中使用 ZUUL,请参考此答案

        尝试了所有可能的解决方案(参考文章和 n 个 stackoverflow 问题)并敲了 2 天,终于找到了这个解决方案。感谢 Zuul 文档 - Cookies and Sensitive Headers

        我遇到了同样的问题 - 从 API 响应,set-Cookie 响应标头出现,而从 Angular 代码调用相同的 api,set-cookie 被跳过或忽略。

        在 application.yml 或属性文件的 zuul 配置中使用 'sensativeHeaders' 属性。参考下面代码sn-p

        zuul:
           routes:
              users:
              path: /myusers/**
              sensitiveHeaders:
              url: https://downstream
        

        没有在拦截器中设置任何额外的标头或属性,例如“withCredentials”。

        【讨论】:

          【解决方案5】:

          我们需要在获取 set-cookies 标头时再检查一个选项,

          set-cookie: SESSIONID=60B2E91C53B976B444144063; Path=/dev/api/abc; HttpOnly
          

          同时检查 Path 属性值。这应该与您的 API 起始上下文路径相同,如下所示

          https://www.example.com/dev/api/abc/v1/users/123
          

          或在不确定上下文路径时使用以下值

          Path=/;
          

          【讨论】:

            猜你喜欢
            • 2020-05-21
            • 2021-05-30
            • 1970-01-01
            • 2017-08-30
            • 2022-11-26
            • 2012-12-06
            • 2014-07-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多