AngularJS 忽略 HTTP 标头中的 Set-Cookie

Posted

技术标签:

【中文标题】AngularJS 忽略 HTTP 标头中的 Set-Cookie【英文标题】:Set-Cookie in HTTP header is ignored with AngularJS 【发布时间】:2013-02-08 04:18:09 【问题描述】:

我正在开发一个基于客户端的 AngularJS 和服务器端的 API(Tomcat + Jersey for WS)的应用程序。

我的 API 的某些路径受到限制,如果用户没有会话,则返回的响应状态为 401。在客户端,拦截 401 http 状态以将用户重定向到登录页面。

用户通过身份验证后,我会在服务器端创建会话

httpRequest.getSession(true);
并且发送给客户端的响应在其标头中确实包含 Set-cookie 指令: 设置 Cookie:JSESSIONID=XXXXXXXXXXXXXXXXXXXXX;域=本地主机;路径=/api/; HttpOnly

问题是cookie永远不会放在客户端。当我检查 localhost 域的 cookie 时,它​​是空的,因此下一个请求的标头中没有此 cookie,客户端仍然无法访问我的 API 的受限路径。

客户端和服务器在同一个域中,但它们没有相同的路径和相同的端口号:

客户:http://localhost:8000/app/index.html

服务器:http://localhost:8080/api/restricted/

附加信息:双方都启用了 CORS:

"Access-Control-Allow-Methods", "GET, POST, OPTIONS"
“访问控制允许来源”,“*”
"Access-Control-Allow-Credentials", true

让 Set-cookie 正常工作的任何想法? 它是 AngularJS 相关的问题吗?

【问题讨论】:

我不确定它是否仍然相关,但让您的 API 依赖于状态是一个糟糕的设计选择 (tm)。您应该使您的 API 无状态以避免将来出现问题。 【参考方案1】:

我找到了一个issue in AngularJS,可以帮助我继续前进。

客户端似乎没有设置"Access-Control-Allow-Credentials" : true。 指令$httpProvider.defaults.withCredentials = true 被忽略。

我将 $resource 调用替换为简单的 $http 调用,并在配置参数中使用 withCredentials:true。

【讨论】:

如果您找到了解决方案,请不要忘记发布答案。如果您的客户端是移动客户端,并且您使用的是 phonegap/cordova 或本机客户端,您是否尝试覆盖 origin/refferer 标头并使用恒定来源? 您可以在后端获取来源,然后发送相关标头。这是一个 express.js 示例: var origin = req.headers.origin; res.header('Access-Control-Allow-Origin', origin); 您似乎同时发布了一个答案和一个问题作为答案。要提出新问题,请使用页面顶部的“”按钮。如果它有助于提供上下文,您可以链接到这个问题。 您必须为“Access-Control-Allow-Origin”显式返回正确的域,“”当“Access-Control-Allow-Credentials”为真时。不允许使用星号。因此,人们要拥有多个允许的来源,就是让一个函数拦截请求来源,验证该来源是否在您的允许列表中,然后在“Access-Control-Allow-Origin”中设置请求来源。例如我希望 localhost 和 example.com 工作。所以当一个请求从 localhost 进来时,我的 header 必须是 "Access-Control-Allow-Origin", "localhost", 例如.com "Access-Control-Allow-Origin", "example.com"【参考方案2】:

我已经设法解决了一个与您的问题非常相似的问题。 我的游戏!后端尝试设置我无法在 Angular 中捕获或通过浏览器存储的会话 Cookie。

实际上,解决方案涉及到这个和那个。

假设您已经解决了最初的问题,只能通过将特定域添加到 Access-Control-Allow-Origin 并删除通配符来解决,接下来的步骤是:

    您必须从 Set-Cookie 标头中删除 HTTP-Only,否则您将永远无法收到由您的 Angular 代码“生成”的 cookie此设置已经在Firefox,虽然不在 Chrome 中

    要使其也适用于 Chrome,您需要:

    a) 使用 WS “托管”的域在 cookie 中发送与 localhost 不同的域。你甚至可以使用像.domain.com 这样的通配符来代替ws.domain.com

    b) 那么您需要调用您在 cookie 中指定的域,否则 Chrome 将不会存储您的 cookie

    [可选] 我会删除 /api 路径以支持 /

这应该是诀窍。 希望对你有所帮助

【讨论】:

我认为问题在于路径是 /api 并且应该是 / 当您说“在 cookie 中发送与 localhost 不同的域,使用您的 WS 被“托管”的域”时,您是指前端主机还是后端主机? 通常它应该是您的后端,但这实际上取决于您的服务器架构。你不知道你的WS在哪里吗?【参考方案3】:

在客户端的发布请求中,确保添加以下内容:

对于 jquery ajax 请求:

$.ajax(
  url: "http://yoururlgoeshere",
  type: "post",
  data: "somedata",
  xhrFields: 
    withCredentials: true
  
);

使用 Angular 的 $http 服务:

$http.post("http://yoururlgoeshere", "somedata", 
  withCredentials: true
);

【讨论】:

我发现在我的情况下 xhrFields:... 会导致一些问题(我的服务器以未知来源*之类的方式响应),所以我改用它 beforeSend: function(xhr) xhr.withCredentials = true; 并且它工作正常 我花了一天多的时间才意识到withCredentials = true 不仅告诉浏览器在请求时附加cookie,而且还告诉它在响应时设置新的cookie。这与 fetch 函数的 ajax 选项相同,其中必须应用 credentials: 'include' 以使浏览器根据请求和响应发送和设置 cookie。【参考方案4】:

您需要同时在服务器端和客户端工作。

客户

通过以下方式之一将$http config withCredentials 设置为true

    每个请求

    var config = withCredentials: true;
    $http.post(url, config);
    

    对于所有请求

    angular.module("your_module_name").config(['$httpProvider',
      function($httpProvider) 
        $httpProvider.interceptors.push(['$q',
          function($q) 
            return 
              request: function(config) 
                config.withCredentials = true;
                return config;
              
            ;
          
        ]);
      
    ]);
    

服务器

将响应头Access-Control-Allow-Credentials设置为true

【讨论】:

不需要创建拦截器,现在可以使用这个$httpProvider.defaults.withCredentials = true; 我花了一天多的时间才意识到withCredentials = true 不仅告诉浏览器在请求时附加cookie,而且还告诉它在响应时设置新的cookie。这与 fetch 函数的 ajax 选项相同,其中必须应用 credentials: 'include' 以使浏览器根据请求和响应发送和设置 cookie。 IE。当发送login ajax 请求到专用api 时,虽然不需要cookie,但必须设置withCredentials=true 否则接收到的sessionid cookie 将可用但浏览器不会保存它。【参考方案5】:

添加HttpOnly 表示浏览器不应该让插件和javascript 看到cookie。这是最近用于更安全浏览的约定。应该用于 J_SESSIONID 但可能不在此处。

【讨论】:

即使HttpOnly属性不存在,客户端仍然没有设置cookie。 你确定吗?它是 /api/session-only cookie。一项实验是使用response.encodeURL 作为您的链接。然后第一个答案可能仍然使用“...?JSESSIONID=...”而不是cookie。但后续调用应该设置 cookie。您是否在浏览器中查看过 cookie? 是的,当我使用 Chrome 查看 cookie 是否已在浏览器中设置和/或是否存在于请求和响应标头中时。 cookie 是 Session only 是的。 我不知道是否未设置 localhost/api 的 cookie。您可以尝试删除 HttpOnly(会很有趣),或者将 cookie 设置为 localhost/ 而不是 localhost/api(不太可能)。 同时删除 Secure;如果您希望浏览器能够在随后的 XHR 调用中存储和使用 cookie,则从 Set-Cookie (如果出现)。【参考方案6】:

刚刚解决了这样一个问题。

我正在这样做,但没有工作......:

  $cookies.put('JSESSIONID', response.data);

cookies 保存在浏览器中,但是当我发送一个新请求时,所有的 cookie 都被发送了,除了我的。 (我的 cookie 是 JSESSIONID)

然后我查看了 chrome 检查器,发现了这个:

问题是那不是正确的路径!!!

然后我尝试了这个,我的 cookie 被发送了。耶! :

$cookies.put('JSESSIONID', response.data, 'path':'/');

我不知道这是否是你的情况,但这对我有用。

问候!

【讨论】:

以上是关于AngularJS 忽略 HTTP 标头中的 Set-Cookie的主要内容,如果未能解决你的问题,请参考以下文章

如何将标头动态传递给 angularjs 的 $resource

访问 API 中的自定义标头

AngularJS $http ajax 不遵循 Location 标头

使用 AngularJS 发送 HTTP 标头请求

忽略所有 HTTP.OPTIONS 调用的身份验证是不是安全?

Angularjs设置授权标头