通过REST API验证/更改密码

时间:2011-11-22 17:58:27

标签: validation rest restful-url restful-architecture

我想通过REST API更改用户密码。这不是忘记或重置密码功能,而是登录用户想要更改密码。

表单需要当前密码,新密码和新密码的确认。但是,我想在用户填写时验证每个表单字段。这对于newPasswordconfirmNewPassword(客户端)来说是微不足道的,但不适用于currentPassword。目前正通过PUT /users/:id对User对象执行更新。如果传递了密码参数,我会检查currentPassword参数并确保它在保存之前是正确的。但是,对于验证,我不确定最佳方法。

我也有POST /users/validate - 不确定这是否也是最好的。这将验证用户对象的创建和更新,但仅验证属于用户对象的字段(emailusernamepassword)。 currentPassword不是其中之一。想知道如何处理这个问题。我考虑过的一些事情:

POST /users/check_passwordPOST /users/validate(如果传递了该参数,则添加currentPassword验证,并检查currentPassword是否与用户现有密码匹配)和 POST /users/:id/validate(对现有用户进行单独验证,需要currentPassword)。

任何想法或建议都将不胜感激。我的第一个应用程序只通过REST API公开功能。

5 个答案:

答案 0 :(得分:8)

我首先要指出的是,身份验证通常是在REST模型之外处理的。当用户提供他们的凭证时,他们没有提供他们的帐户对象的状态(REST)的呈现;他们也没有得到这样的回应。由于用户帐户资源状态不包括“当前”和“新”密码,因此在请求中提供“当前”和“新”密码永远不会真正适合REST模型,但专业人员通常会描述RESTfulness的“连续统一体”,其中一些API完全是RESTful的,而另一些则介于RPC(远程过程调用)和REST之间。

拥有处理数据模型服务的API的RESTful组件以及处理用户帐户的API的更多RPC组件并不罕见。你可以在两者之间做出决定。如果您的项目包括管理多个用户帐户的超级用户,我建议尝试将其纳入REST API。如果每个用户只管理自己的帐户,我建议使用RPC。

如果您决定使用REST进行帐户管理,那么您必须选择适当的HTTP方法(GET,POST,DELETE,HEADERS等)。显然,您需要一种能够影响服务器更改的方法(POST,PUT,DELETE等)。与上面的用户orbfish的结论相反,我会说在某些限制条件下,PUT将是一种合适的方法。

来自RFC 2616,它正式定义了我们的HTTP方法:

  

“方法也可以具有”幂等“的属性(除了错误或过期问题)N> 0个相同请求的副作用与单个请求的相同。方法GET,HEAD, PUT和DELETE共享这个属性。另外,方法OPTIONS和TRACE不应该有副作用,因此本质上是幂等的。“

此处的幂等性意味着如果我们连续发出相同的请求 n 次,那么 n 请求影响下的服务器状态将是相同的作为第一个请求的影响下的服务器的状态。用户orbfish正确地指出,如果我们提出请求:

PUT /users/:id/account {current-password: 'a', new-password: 'b'}

并重复一遍:

PUT /users/:id/account {current-password: 'a', new-password: 'b'}

我们的第一个请求应该收到指示成功的响应,而我们的第二个请求应该收到指示失败的响应。但是,PUT的幂等性只要求服务器的状态在两个请求之后都是相同的。它是:在第一次请求后,用户的密码为“b”,在第二次请求后,用户的密码为“b”。

我提到了上述限制。您可能希望在 m 尝试更改密码失败后锁定用户;这将提供防止暴力密码攻击的安全性。但是,这会破坏请求的幂等性:发送一次有效的密码请求并更改密码,将其发送 m 多次,服务器会将您锁定。

通过指定PUT方法,您可以告诉所有客户端根据需要多次发送请求是安全的。如果我作为客户端发送PUT请求并且我们的连接被中断而我没有收到您的响应,我知道再次发送我的PUT是安全的,因为它是幂等的:幂等意味着如果你收到两个请求它与您的服务器相同,只需接收一个。但是如果你要把我锁定在一个不成功的请求中,那么在我知道你是否收到第一个请求之前发送第二个请求是不安全的。

因此,您可以考虑使用PATCH或POST。我建议使用PATCH。虽然POST被描述为将新资源附加到列表或将数据附加到现有资源,但PATCH被描述为对已知URI上的资源的“部分更新”。与PUT不同,PATCH不一定是幂等的。

答案 1 :(得分:6)

我不喜欢/ check_password或/ validate,因为它们是动词;你的第一个“更新用户”是更好的REST。

您可以将currentPassword作为未加载字段添加到User对象,或作为Authentication标头(用户名:密码)的一部分。

但是,我肯定会将其从PUT更改为POST,因为使用相同currentPassword的相同调用不能成功两次(PUT是幂等的)。

答案 2 :(得分:2)

您正在更改用户资源的属性(即密码)。如果您使用HTTP Basic进行授权,则表示您已经提供了当前密码,因此无需重复。我只想用新密码输出整个用户资源。例如:

PUT /users/fiddlerpianist HTTP/1.1
Content-Type: application/json
Authorization: Basic ZmlkZGxlcnBpYW5pc3Q6bXlub3Rzb2F3ZXNvbWVvbGRwYXNzd29yZA==

{
    "password": "My awesome new password that no one will ever be able to guess!"
}

以这种方式执行此操作的另一个好处是,只要您是具有修改用户资源的访问权限的凭证用户,您就不必必须提供旧密码。也许你是一个客户支持专家,他从不应该询问客户的旧密码,但他们要求通过电话更改密码(在他们向你证明了他们的身份之后你就已经证明了你对系统的认同。)

在这种情况下,您希望避免使用非幂等请求(例如PUT或PATCH),因为这会导致结果不确定的响应(假设服务器为非幂等请求返回500 ...你作为客户端不知道服务器离开你的资源的状态。)

编辑添加:请注意,在RESTful应用程序中,没有“登录”的概念。从客户端到服务器的通信完全是无状态的(它是传递状态的有效负载和方法)。此外,确实不需要以您描述它的方式进行验证的概念,因为更改资源状态的请求可以通过200 OK(如果有效)或400 Bad Request(如果无效)来满足)。

答案 3 :(得分:2)

另一种选择是在user上创建代理资源。如果您正在使用HATEOAS,则可以从user/x/pwdchange资源链接到user。我想澄清pwdchange被认为是名词/资源,而不是动词:

GET /user/jsmith/pwdchange     List of password change requests (history)
POST /user/jsmith/pwdchange    Create password change request, return id=1
GET /user/jsmith/pwdchange/1   Get password change resource, which would
                               include the outcome (success, failure, etc)

因此,简而言之,我正在创建一个名为“pwdchange'这完全符合问题域的REST视图。

答案 4 :(得分:1)

您可能会考虑为何需要在输入后立即验证当前密码。我没见过网站那样做。其次,拥有一个只能验证某些东西的服务是完全没问题的。它被称为实用的诗句,打败自己试图成为RESTFul