使用HTTP身份验证

Cam*_*ron 20 php jquery

我正在使用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) {

    }   
});
Run Code Online (Sandbox Code Playgroud)

我已经在此插件的代码中使用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))
    }

}
Run Code Online (Sandbox Code Playgroud)

我有几个问题:

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

问题2:如前所述,我的API设计基于上面的Cake插件,但从我可以看到,他们总是在每个请求中传递用户名和密码,然后决定是否使用它,如果用户已登录.是这个对吗?对我来说,似乎更有可能分别处理身份验证和数据请求.否则,我必须在我的JavaScript中存储用户名和密码,以便我可以随每个请求发送它.

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

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

Fra*_*ila 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中.例如,许多网站都有一个/profile网址,但您看到的个人资料取决于隐身的"谁登录"状态.这不是RESTful.相反,url应该包含用户,例如/profiles/username.您是否真正了解该资源取决于您是否有权查看该资源,这取决于您是否经过身份验证的用户身份验证.身份验证是与资源标识分开的一个层.(例如,假设你有一个管理员用户可以看到其他人的个人资料.如果你只有一个/profile网址,你会如何为他设计一个查看其他个人资料的方法?显然,资源的存在与能力不同看到它,从谁看着它.)

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

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

这样就在标题中留下了HTTP.我们可以使用传统的基于cookie的身份验证,我们在特殊URL上"登录"并获取令牌,或者HTTP本地支持的HTTP身份验证.

HTTP身份验证在RESTful原则方面要优越得多,因为它是无状态的.(这使得您的第三个问题荒谬 - 没有"登录"或"已注销"状态 - 您要么为请求提供了正确的凭据,要么您没有!)您无需确保首次访问一个特殊的网址,并获得了一个你需要保存的魔术令牌(可能会突然过期),你每次只需要发出一个带有你的凭据的请求.如果您无权访问资源,则会有一个特定的HTTP响应代码(401)和标头(WWW-Authenticate,Authorization)和一组明确定义的行为.它还可以使用不同的授权方法进行扩展.还有相当多的javascript支持,至少如果你坚持使用XmlHTTPRequest.(哎呀,jQuery 1.7.2甚至有用户名和密码选项,所以你甚至不需要自己进行base64编码!)

缺点是常用且受到良好支持的唯一HTTP身份验证方法是Basic和Digest,并且它们都不是很安全.如果你只使用https,它们可能很好,但除此之外它们很糟糕.

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

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


Ame*_*lia 8

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

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


问题2:如前所述,我的API设计基于上面的Cake插件,但从我可以看到,他们总是在每个请求中传递用户名和密码,然后决定是否使用它,如果用户已登录.是这个对吗?对我来说,似乎更有可能分别处理身份验证和数据请求.否则,我必须在我的JavaScript中存储用户名和密码,以便我可以随每个请求发送它.

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


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

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

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

千万不能拿一个value: true领域是登录,因为这是一个安全漏洞.