20

我正在用 PHP 构建一个 REST API 以使用基于 JavaScript 的应用程序。所有请求都作为 JSON 处理,有些请求需要身份验证。

一个示例请求是:

$.ajax({
    type: 'GET',
    url: 'http://domain.com/api/posts/recent.json',
    headers: {
        Authorization: 'X-TRUEREST ' + $.param({
            username: 'johndoe',
            password: '********'
        })
    },
    success: function(response){
            // Handle response here (includes auth errors too)
    },
    error: function(a,b,c) {

    }   
});

我基于此插件中的代码使用 HTTP 身份验证标头:https ://github.com/kvz/cakephp-rest-plugin/blob/master/Controller/Component/RestComponent.php#L532

如您所见,它们使用标头传递的参数,然后用于将用户登录到系统(如果还没有的话)。据我所知,他们希望身份验证凭据与数据请求一起传递。

这方面的一个例子是(注意在我的示例应用程序中没有使用 CakePHP):

if ( $loggedIn ) { // logged in is true of false based on session existing

    // Then return the JSON as normal

} else {

    // Grab HEADERS INFO

    $headers = $_SERVER['HTTP_AUTHORIZATION'];
    $parts = explode(' ', $_SERVER['HTTP_AUTHORIZATION']);

    // Then use the parts to find a user in the DB... and populate $user

    if($user){

        $this->Auth->login($user); // login the user with our authentication code

        // Then return JSON as normal

    } else {
        print json_encode(array('auth'=>false))
    }

}

我有几个问题:

问题 1:为什么要使用 HTTP Authentication 和 headers?据我所知,除非我不正确地使用它们,否则他们不会为我提供任何东西?为什么不直接在 JS 的数据参数中传递用户名和密码呢?

问题 2:如前所述,我的 API 设计基于上面的 Cake 插件,但据我所知,他们总是在每个请求中传递用户名和密码,然后在用户登录时决定是否使用它。是这对吗?对我来说,身份验证和数据请求似乎更有可能分开处理。否则我将不得不将用户名和密码存储在我的 JavaScript 中的某个地方,以便我可以随每个请求一起发送它。

这让我想到了下一个问题......

问题3:如何知道用户是否登录?我可以将变量设置为 true,然后将其作为 JSON 的一部分传递给每个请求,但是会话可以与外部设备一起使用吗?

4

4 回答 4

22

如果你正在设计一个 REST api,你应该遵守REST 原则。有两个重要的认证需要强调:

  1. 通过 URI 识别资源
  2. 通过服务器提供的链接进行状态转换。

要遵守原则 1,您需要将身份验证排除在 URI 之外http://example.org/list-of-stuff?auth-token=12345不是与 不同的资源http://example.org/list-of-stuff?auth-token=67890因此它不应具有不同的 URI。拥有不同的 URI 也使得无法跨不同用户缓存资源。

通常,如果资源会因某些条件而有所不同,则该条件需要在 URI 中。例如,许多网站都有一个/profileurl,但您看到的配置文件取决于不可见的“谁登录”状态。这不是 RESTful。相反,url 应该包含用户,例如/profiles/username. 您是否真正能够看到该资源取决于您是否有权查看它,这取决于您是否被认证为被授权的用户。身份验证是与资源识别不同的层。(例如,假设您有一个管理员用户可以查看其他人的个人资料。如果您只有一个/profileurl,您将如何设计一种方法让他查看其他配置文件?显然,资源的存在与查看它的能力以及查看它的人不同。)

所以我们已经确定不应该通过 URI 中的参数进行身份验证。由于我们使用的是 HTTP,我们可以在标头中提供它,也可以在 HTTP 本身之外提供它。

虽然不是很常见,但一些 REST API 使用客户端证书在 SSL 层处理身份验证。从技术角度来看,这很棒,但用户体验令人费解和糟糕。虽然这篇文章是 2008 年的,但没有任何改进。这种方法也不容易从浏览器 JS 编写脚本,即使在浏览器之外,编写必须提供客户端证书的应用程序也很麻烦。服务器端的开发也很困难,因为大多数 Web 脚本环境根本无法让您轻松访问 SSL 层的东西,更不用说更深奥的 SSL 功能,如客户端证书。您的应用程序可能无法知道随请求提供的证书身份。

这样就在标头中留下了 HTTP。我们既可以使用传统的基于 cookie 的身份验证,也可以使用 HTTP 原生支持的 HTTP 身份验证,在特殊 URL 上“登录”并获取令牌。

HTTP 身份验证在 RESTful 原则方面要优越得多,因为它是无状态的。(这使您的第三个问题变得荒谬——没有“登录”或“注销”状态——您要么为请求提供了正确的凭据,要么没有!)您不需要确保您第一次访问一个特殊的 url 并获得了一个需要保存的魔法令牌(可能会突然过期),您每次只需使用您的凭据发出请求。如果您无权访问资源,则有特定的 HTTP 响应代码 (401) 和标头(WWW-Authenticate、Authorization)和一组明确定义的行为。它也可以通过不同的授权方法进行扩展。也有相当多的 javascript 支持,至少如果您坚持使用 XmlHTTPRequest。(见鬼,jQuery 1.7。

缺点是唯一常用且支持良好的 HTTP 身份验证方法是 Basic 和 Digest,它们都不是很安全。如果您只使用 https ,它们可能很好,但否则它们很糟糕。

此外,HTTP 身份验证对于人类正常使用浏览器毫无用处:没有自定义登录页面,无法呈现“忘记密码”功能或其他身份验证自定义,并且浏览器制造商仍然没有提供简单的“注销”方法(即,忘记当前领域的凭据)!

基于 Cookie 的身份验证为您提供了最大程度的控制,但您需要保持服务器端和客户端身份验证状态,并担心一大堆其他安全问题,例如会话固定,甚至是会话的组成部分!是 IP 地址、用户代理还是某种组合?在我们过期之前,经过身份验证的会话应该有效多长时间?如果涉及代理并且 IP 地址经常更改怎么办?魔法令牌过期了怎么办?当用户没有被授权时,我们该怎么办?(您不能使用 HTTP 401 响应——您需要定义自己的特定于您的站点的方法。)本质上,您需要定义自己的复杂会话和身份验证协议或采用其他人的。至少对于 HTTP 身份验证,您唯一需要担心的是攻击者会读取您的 Authenticate 标头,而使用 SSL 可以解决该问题。

于 2013-02-03T20:47:53.550 回答
8

问题 1:为什么要使用 HTTP Authentication 和 headers?据我所知,除非我不正确地使用它们,否则他们不会为我提供任何东西?为什么不直接在 JS 的数据参数中传递用户名和密码呢?

此处的 HTTP 身份验证似乎是开发人员的个人选择。您可以使用OpenID。您可以使用令牌。您可以使用 HTTP 标头身份验证。您可以使用会话/ cookies。哎呀,如果您愿意,您甚至可以使用RSA 密钥身份验证SSL 客户端证书


问题 2:如前所述,我的 API 设计基于上面的 Cake 插件,但据我所知,他们总是在每个请求中传递用户名和密码,然后在用户登录时决定是否使用它。是这对吗?对我来说,身份验证和数据请求似乎更有可能分开处理。否则我将不得不将用户名和密码存储在我的 JavaScript 中的某个地方,以便我可以随每个请求一起发送它。

您永远不应该以纯文本形式公开任何内容的用户名和密码(从技术上讲,您甚至不应该这样存储它,但这不是问题所在)。使用带有 API 的公共令牌或唯一的 cookie ID。就个人而言,我用来查看用户是否登录了任何东西。如果必须,让 AJAX 提交,然后在内部将 ID 映射到令牌。$_SESSION["id"]


问题3:如何知道用户是否登录?我可以将变量设置为 true,然后将其作为 JSON 的一部分传递给每个请求,但是会话可以与外部设备一起使用吗?

我有一个用于此答案的函数。理想情况下,您应该使用会话检查有效登录,并将索引的值(例如$_SESSION["id"])设置为该帐户唯一的值(通常,这是他们的数据库 ID)。

编辑:阅读 OP 上的评论后,您必须确保会话 cookie 在发出请求之前已登录的任何设备上

不要字段作为value: true登录名,因为这是一个安全漏洞。

于 2013-02-03T19:06:19.117 回答
4

我认为您在使用 URI 将信息传递给 REST api 和使用 http 简单身份验证授权对特定服务器的访问之间进行混合(可能是故意的)。

HTTP 身份验证也可用于处理用户,但这不是一个很好的选择(如其他答案中所述)。用户、会话、安全令牌等应该由应用程序逻辑处理,而 api 访问也可以由服务器保护(使用 http 简单或摘要安全、NTLM、IP 过滤或其他)。让 Web 服务器承担一些安全负担/分离问题/某种测试站点/等等。

一个示例场景正在处理测试区域服务器中受保护的服务器访问的 http 安全性,而应用程序 REST api 将接受并处理通过服务器安全检查的每个人的不同用户、令牌、权限级别等。

寻找这个问题以获得更多参考

于 2013-02-03T22:51:36.673 回答
1

问题 1:为什么要使用 HTTP Authentication 和 headers?据我所知,除非我不正确地使用它们,否则他们不会为我提供任何东西?为什么不直接在 JS 的数据参数中传递用户名和密码呢?

查询字符串是 URI 的一部分。URI 是 REST 的资源标识符,因此它是资源状态的一部分。用户的身份、凭据等......是客户端状态的一部分。通过将客户端状态添加到 URI 来混合客户端状态和资源状态将违反 REST 的无状态约束。其他请求的数据部分,如 POST、PUT、DELETE 用于描述资源表示,因此您不应使用它发送 auth 数据。发送 headers 是最好的方法,因为 HTTP 有一个专用的 Authorization header,你应该使用它。

问题 2:如前所述,我的 API 设计基于上面的 Cake 插件,但据我所知,他们总是在每个请求中传递用户名和密码,然后在用户登录时决定是否使用它。是这对吗?对我来说,身份验证和数据请求似乎更有可能分开处理。否则我将不得不将用户名和密码存储在我的 JavaScript 中的某个地方,以便我可以随每个请求一起发送它。

您的服务器应该对每个请求进行身份验证,以保持无状态。我不确定这个库的逻辑,我认为登录是描述发生了什么的错误术语,但从 REST 的角度来看,实现细节并不重要,只要它正确使用 HTTP 标准,并且通信是无状态的。自从您提出问题以来,代码已更改。

问题3:如何知道用户是否登录?我可以将变量设置为 true,然后将其作为 JSON 的一部分传递给每个请求,但是会话可以与外部设备一起使用吗?

服务器不关心客户端是否登录,因为它是客户端状态。如果您发送需要权限的请求,那么您必须发送验证用户所需的数据。

于 2015-10-06T15:28:13.797 回答