2013-01-31 215 views
20

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

一个例子请求是:

$.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

正如你可以看到他们采取传递的参数与头和那么如果用户还没有登录系统,他们将被用于登录系统。从我可以告诉他们期望的auth凭证传递与数据请求。

这方面的一个例子是(在我的示例应用程序使用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身份验证和头文件?据我所知,除非我错误地使用它们,否则它们不会提供任何东西?为什么不在JS的数据参数中传递用户名和密码?

问题2:如前所述我已经基于上述蛋糕插件我的API设计,但是从我所看到的,他们总是每个请求传递用户名和密码,然后决定使用与否如果用户登录,这是正确的吗?对我而言,认证和数据请求应该分开处理的可能性更大。否则,我将不得不将用户名和密码存储在我的JavaScript中,以便我可以在每个请求中发送它。

这使我下一个问题......

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

+0

大多数RestAPI使用令牌或密钥。我正在构建一个需要身份验证的应用程序,然后为用户生成一个令牌。功能很像一个cookie。没有什么说你必须使用标题。您可以使用标题,cookie或参数进行GET或POST – Cfreak

+0

@Cameron,您对外部设备有何意义,为什么它不适用于会话? – Yoggi

+0

@Yoggi外部在会话中由服务器设置,但请求来自其他地方,不像普通的HTTP请求。我并不是说这不会起作用,只是询问可以完成这些操作的不同方式以及HTTP Auth提供的仅仅通过数据参数传递凭据的方式。 – Cameron

回答

8

问题1:为什么要使用HTTP身份验证和头文件?据我所知,除非我错误地使用它们,否则它们不会提供任何东西?为什么不在JS的数据参数中传递用户名和密码?

HTTP身份验证在这里似乎是开发人员的个人选择。您可以使用OpenID。你可以使用一个令牌。你可以使用HTTP头认证。您可以使用sessions/cookies。哎呀,如果你喜欢,你甚至可以使用RSA Key AuthenticationSSL Client Certificates


问题2:如前所述我已经基于上述蛋糕插件我的API设计,但是从我所看到的,他们总是每个请求传递用户名和密码,然后决定使用它或如果用户登录,则不行。这是否正确?对我而言,认证和数据请求应该分开处理的可能性更大。否则,我将不得不将用户名和密码存储在我的JavaScript中,以便我可以在每个请求中发送它。

你不应该揭露任何明文的用户名和密码,不断(技术上,你甚至不应该将其存储为这样的,但是这并不是问题是关于什么的)。使用带有API的公共令牌或唯一的Cookie ID。我个人使用$_SESSION["id"]来查看用户是否登录了任何东西。如果需要,让AJAX提交并将ID映射到内部的令牌。


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

我有一个功能,我用这个链接in this answer。理想情况下,您应该使用会话检查有效登录,并将索引值(如$_SESSION["id"])设置为该帐户唯一的值(通常为数据库ID)。

编辑:后阅读的OP的意见,你就必须确保会话cookie是在登录的任何设备上之前提出请求

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

22

如果你正在设计一个REST API,你应该坚持REST principles。有突出的认证两个重要的:通过URI资源

  1. 鉴定通过服务器提供的链接
  2. 状态转换。

要遵守原则1,您需要保持您的身份验证超出您的URIhttp://example.org/list-of-stuff?auth-token=12345http://example.org/list-of-stuff?auth-token=67890不是一个不同的资源,所以它不应该有不同的URI。拥有不同的URI也会导致无法跨不同用户缓存资源。

一般来说,如果资源会根据某些条件而不同,则该条件需要位于URI中。例如,许多网站的网址为/profile,但您看到的配置文件取决于不可见的“谁登录”状态。这是而不是RESTful。而url应该包含用户,例如/profiles/username。无论您是否真正到达,请参阅该资源取决于您是否有权查看它,这取决于您是否通过了授权的用户身份验证。认证是与资源标识分离的一个单独的层。 (例如,假设你有一个管理员用户可以看到其他人的个人资料,如果你只有一个url,你会如何设计一个方法让他看到其他的个人资料?显然,资源的存在与能力有些不同看到它和从谁看它。)

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

虽然这不是很常见,但某些REST apis使用客户端证书在SSL层处理认证。从技术角度来看这很好,但是user experience is baffling and terrible。虽然这篇文章是从2008年开始的,但没有任何改进。这种方法也不容易从浏览器JS脚本化,甚至在浏览器之外编写必须提供客户端证书的应用程序也很麻烦。服务器端的开发也很困难,因为大多数Web脚本环境并不能让您轻松访问SSL层的东西,更不用说更加深奥的SSL功能,比如客户端证书。您的应用程序可能无法知道该请求提供了哪种证书标识。

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

HTTP身份验证在RESTful原则方面远远优于它,因为它是无状态的。 (这会让你的第三个问题变得荒谬 - 没有“登录”或“注销”状态 - 你为请求提供了正确的凭据,或者你没有提供!)你不需要确保你第一次访问一个特殊的URL,并得到一个你需要保存的魔法令牌(可能会突然过期),你每次只需发送一个包含你的凭证的请求。如果您无权访问资源,则会有特定的HTTP响应代码(401)和标头(WWW-Authenticate,Authorization)以及一组明确定义的行为。它也可以通过不同的授权方法进行扩展。至少如果你坚持使用XmlHTTPRequest,也会有相当数量的javascript支持。 (嘿,jQuery 1.7.2甚至有用户名和密码选项,所以你甚至不需要base64编码它自己!)

缺点是唯一的HTTP验证方法是常用的和良好的支持是基础和摘要,并且它们都不是很安全。如果您只使用https,那么它们可能很好,但否则它们很糟糕。

此外,HTTP认证对于人类普通浏览器的使用没有用处:没有自定义登录页面,没有办法提供“忘记密码”功能或其他认证自定义,仍然浏览器制造商没有提供简单的方法来“注销”(即忘记当前领域的凭证)!

基于Cookie的身份验证为您提供最大程度的控制,但是你需要保持服务器端和客户端的认证状态和担心的其他安全问题一大堆,如session fixation甚至什么是会话!它是IP地址,用户代理,一些组合?经过验证的会话在我们到期之前应该有效多久?如果涉及代理并且IP地址频繁更改会怎样?当魔法令牌过期时我们该怎么办?当用户未被授权时我们应该怎么做? (您不能使用HTTP 401响应 - 您需要定义自己的特定于您的站点的方法。)本质上,您需要定义自己的复杂会话和身份验证协议或采用其他人的方法。至少在使用HTTP身份验证时,您唯一需要担心的是攻击者读取您的Authenticate头文件,并使用SSL解决该问题。

4

我认为你正在混合(也许是故意的)使用URI将信息传递给REST api,并使用http simple Authentication授权访问特定服务器。

HTTP身份验证也可以用来处理用户,但它不会是一个非常好的选择(因为在其他答案中已经生动)。用户,会话,安全令牌等应该由应用逻辑处理,而api访问也可以安全地由服务器保护(使用http简单或摘要安全,NTLM,IP过滤或其他)。让Web服务器承担一些安全负担/分离问题/某种测试站点/等等。

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

看看这个question更多参考

1

问题1:为什么要使用HTTP认证和头?至于 我可以告诉,他们不提供任何东西,除非我错误地使用它们 ?为什么不在数据的参数 中传递用户名和密码?

查询字符串是URI的一部分。 URI是REST的资源标识符,因此它是资源状态的一部分。用户的身份,凭证等是客户状态的一部分。通过向URI添加客户端状态来混合客户端状态和资源状态将违反REST的无状态约束。其他请求的数据部分,如POST,PUT,DELETE用于描述资源表示,所以您不应该使用它发送授权数据。发送标头是最好的方法,并且由于HTTP具有专用的授权标头,因此您应该使用它。

问题2:如前所述我已经基于该蛋糕我的API设计上面 插件,但是从我所看到的,他们总是每个请求传递用户名 和密码,然后决定使用与否如果 用户已登录,这是正确的吗?对我而言,似乎更有可能 认证和请求数据应分开处理 。否则,我将不得不将用户名和密码 存储在我的JavaScript的某处,以便我可以在每次请求时发送它。

您的服务器应验证每个请求,以保持无状态。我不确定这个库的逻辑,我认为login是描述发生的事情的错误术语,但是从REST的角度来看,只要正确使用HTTP标准并且通信是无状态的,实现细节并不重要。自从你问你的问题以来,代码已经改变了。

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

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