多个HTTP授权头?

88

在HTTP消息中是否可以包含多个Authorization头?具体而言,我想包含一个Bearer令牌类型(传递OAuth访问令牌)和一个Basic类型(传递base64编码的用户名:密码)。

GET /presence/alice HTTP/1.1 
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
Authorization: Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk

我认为这是有可能的,只是想向社区确认一下。


多个身份验证方案用于HTTP授权头。 - Nicholas Shanks
7个回答

59

**** 2021年2月更新 *** 请阅读对此回答的评论。他们的一般结论似乎是,一些Web服务器接受多个授权方案,但这违反了RFC 7230/7235 ****

这应该是可行的,您只需要在字段值之间添加逗号,例如:

GET /presence/alice HTTP/1.1 
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM, Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk

这是在RFC7230第3.2.2节“字段顺序”中定义的:

发送者不得生成带有相同字段名称的多个头字段,除非该头字段的整个字段值被定义为逗号分隔列表(即#(values)),或者该头字段是一个众所周知的例外情况(如下所述)。

收件人可以将具有相同字段名称的多个头字段组合成一个“field-name: field-value”对,通过按顺序将每个后续的字段值附加到组合字段值中并用逗号分隔来实现,而不改变消息的语义。 因此,接收具有相同字段名称的标头字段的顺序对于解释组合字段值是很重要的;代理转发消息时不得更改这些字段值的顺序。

我不知道所有的Web服务器是否都接受这一点 - 在撰写本文时,我正在与一位同事辩论它是否应该生效。


6
我认为这应该是被接受的答案。在我的情况下,使用逗号效果完美。基本认证和JWT。 - tedi
26
不行。该部分仅适用于整个字段值被定义为逗号分隔列表的标题,例如Accept-Encoding标头。然而,Authorization标头的字段值并非像那样定义 - uasi
6
@Sam Critchley,标题栏有一个凭证字段,而凭证字段由两部分组成:认证方案和参数/参数列表。参数可以用逗号分隔,但是请注意,整个凭证字段不是列表。(这里“凭证”为复数形式并不重要——它是一个标量值。) - uasi
6
这是错误的实现!请参考RFC,附录C:https://tools.ietf.org/html/rfc7235#appendix-C 授权不是逗号分隔的列表。 如果服务器接受它,那么它就没有按照RFC实现协议。 - Elie Saad
2
@Scott 这是关于 WWW-AuthenticateProxy-Authenticate 头的注释。它们可以包含多个 challenge,但 Authorization 头只能包含一个 scheme - uasi
显示剩余5条评论

40

3
尽管我应该相信你,因为我知道你是谁,但你所说的与规范不符: “在创建其值时,用户代理应该通过选择其认为最安全的授权方案来进行,并根据需要从用户获取凭据。”-具体而言,1)“应该”,2)token68排除了“,”,这意味着逗号不会被解释为令牌的一部分,3)规范中没有说明不能提供多个Auth.头,即两个头由CRLF分隔。参见 https://github.com/nickstenning/nginx-multiauth。 - Nicholas Shanks
14
仅当使用列表语法定义时,才能使用多个标头字段;请参见http://greenbytes.de/tech/webdav/rfc7230.html#rfc.section.3.2.2.p.2 - Julian Reschke
@JulianReschke 我们能否在同一个请求中设置第一个包含基本认证的授权头,并设置第二个包含Bearer认证的授权头? - JarsOfJam-Scheduler
1
不,那将是无效的语法。 - Julian Reschke

11

我有一个类似的问题。这似乎是一个相当常见的问题 (链接到问题)。我最终改变了基于承载令牌的授权头部,使用了一个非标准的头部:

X-Auth:Bearer mF_9.B5f-4.1JqM

这样它只是另一个HTTP头部,基本的HTTP授权将会通过。如果你正在开发自己的API,这应该不是问题。

进一步研究

基于RFC 2617,这里有一些有趣的细节。

用户代理必须选择使用其中一个具有最强认证方案的挑战,并基于该挑战要求用户提供凭据。

请注意,许多浏览器只能识别基本身份验证,并且要求它成为首个呈现的认证方案。服务器应仅在最小可接受情况下包含基本身份验证。


6
现在RFC 2617已经不相关了。你需要查看RFC 7235。 - Julian Reschke

2
如果您在后端使用Python,那么可以简单地将字典传递给Bearer,在后端处理之前进行json.loads。
这样,您就可以在一个授权标头中传递多个值。
例如:传递{"access_token" : access_token, "app_id" : 2}
后端使用json.loads("{"access_token" : access_token, "app_id" : 2}")

1

标题 字段是键值对。只要它们是唯一的,并且您/程序员知道谁是谁,这就可以了。例如:

AuthorizationBearer: Bearer mF_9.B5f-4.1JqM
AuthorizationBasic: Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk

当我的Angular拦截器向Node API发送Authorization111: Bearer xyz123时,API将提取令牌值。
// you can do more validation here, i.e, length
var token = header.headers["authorization111"].toString().split(' ')[1];  

我认为这是一个很好的答案,但是我会加上一个条件来确保值中有一个空格,否则服务器会输出逻辑错误。 - Fiddle Freak
@FiddleFreak 你能详细解释一下吗? - Jeb50
我会在 const arrAuthHeader = req.get('Authorization').split(" "); 停止,然后使用两个 if 条件检查变量 > if (!arrAuthHeader)if(arrAuthHeader.length < 2)。这样你就可以正确地抛出错误。接着只需进行赋值操作 const bearer = arrAuthHeader[0];const token = arrAuthHeader[1]; - Fiddle Freak

1
虽然在Authorization中使用逗号分隔的值在技术上是最好的解决方案,但作为最后的办法,您可以尝试将纯用户凭据作为URL的一部分添加到您选择的httpClient,并发送带有Bearer头的请求。这仅适用于Basic授权,但如果其他方法都不起作用,它可能会起作用。
https://myUsername:strongPassword@server.example.com/presence/alice

由于密码在此处以明文形式使用,这仅用于完整性的考虑,并不是推荐的做法。

-2

可以有多个授权头,我在集成接受多个授权的 API 时也遇到了同样的问题。

这里是使用 React js 调用接受多个身份验证令牌的 API 的示例。

axios.get(Constants.API+Constants.GET_USER,  {  headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
"Authorization": Constants.AUTH_Element + ',' + Constants.AUTH_ORG + ','+ 
Constants.AUTH_USER
}})
.then(function (response) {
    // handle success
    console.log(response);
})
.catch(function (error) {
    // handle error
    console.log(error);
})
.finally(function () {
    // always executed
});

问题是哪个API? - Pacerier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接