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
AngularJS $http ajax 不遵循 Location 标头