122

构建用于重置密码的 RESTful 资源的正确方法是什么?

此资源旨在为丢失或忘记密码的人提供密码重置器。它使他们的旧密码无效并通过电子邮件向他们发送密码。

我有两个选择:

POST /reset_password/{user_name}

或者...

POST /reset_password
   -Username passed through request body

我很确定请求应该是 POST。我不太确定我选择了一个合适的名字。而且我不确定 user_name 是否应该通过 URL 或请求正文传递。

4

9 回答 9

118

未经身份验证的用户

我们在端点PUT上发出请求,api/v1/account/password并要求带有相应帐户电子邮件的参数来标识用户想要重置(更新)密码的帐户:

PUT : /api/v1/account/password?email={email@example.com}

注意: 正如@DougDomeny在他的评论中提到的那样,将电子邮件作为 url 中的查询字符串传递是一种安全风险。GET 参数在使用时不会暴露https(并且您应该始终https为此类请求使用正确的连接),但还涉及其他安全风险。您可以在此博客文章中阅读有关此主题的更多信息。

在请求正文中传递电子邮件将比将其作为 GET 参数传递更安全:

PUT : /api/v1/account/password

请求正文:

{
    "email": "email@example.com"
}

响应具有202可接受的响应含义:

请求已被接受处理,但处理尚未完成。该请求最终可能会或可能不会被执行,因为在实际进行处理时它可能会被禁止。无法从诸如此类的异步操作中重新发送状态代码。

用户将收到一封电子邮件,email@example.com更新请求的处理取决于对电子邮件中的链接采取的操作。

https://example.com/password-reset?token=1234567890

打开此电子邮件中的链接将指向前端应用程序上的重置密码表单,该表单使用链接中的重置密码令牌作为隐藏输入字段的输入(该令牌是作为查询字符串的链接的一部分)。另一个输入字段允许用户设置新密码。用于确认新密码的第二个输入将用于前端验证(以防止拼写错误)。

注意: 在电子邮件中我们还可以提到,如果用户没有初始化任何密码重置,他/她可以忽略电子邮件并继续使用他/她当前的密码正常使用应用程序

当表单使用新密码和令牌作为输入提交时,将进行重置密码过程。表单数据将PUT再次与请求一起发送,但这次包括令牌,我们将用新值替换资源密码:

PUT : /api/v1/account/password

请求正文:

{
    "token":"1234567890",
    "new":"password"
}

响应将是204无内容响应

服务器已完成请求,但不需要返回实体主体,并且可能希望返回更新的元信息。响应可能包括实体头形式的新的或更新的元信息,如果存在,应该与请求的变体相关联。

经过身份验证的用户

对于想要更改密码的经过身份验证的用户,PUT可以在没有电子邮件的情况下立即执行请求(我们正在更新密码的帐户是服务器已知的)。在这种情况下,表单将提交两个字段:

PUT : /api/v1/account/password

请求正文:

{
    "old":"password",
    "new":"password"
}
于 2015-10-28T11:13:17.670 回答
63

更新:(进一步在下面发表评论)

我会去做这样的事情:

POST /users/:user_id/reset_password

您有一组用户,其中单个用户由{user_name}. 然后,您将指定要操作的操作,在本例中为reset_password. 这就像说“为”创建(POST)一个新reset_password动作{user_name}


上一个答案:

我会去做这样的事情:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

您将有两个集合,一个用户集合和一个针对每个用户的属性集合。用户由 指定,:user_id属性由 指定password。该PUT操作更新集合的寻址成员。

于 2010-06-19T21:11:31.150 回答
21

让我们来看看 uber-RESTful。为什么不对密码使用 DELETE 操作来触发重置?有道理,不是吗?毕竟,您实际上是在丢弃现有密码,转而使用另一个密码。

这意味着你会这样做:

DELETE /users/{user_name}/password

现在,有两个重要的警告:

  1. HTTP DELETE 应该是幂等的(一个花哨的词来表示“如果你多次执行它就没什么大不了的”)。如果您正在执行诸如发送“密码重置”电子邮件之类的标准操作,那么您将遇到问题。您可以使用布尔“Is Reset”标志来解决此标记用户/密码的问题。在每次删除时,您都会检查此标志;如果未设置,可以重置密码并发送电子邮件。(请注意,拥有此标志也可能有其他用途。)

  2. 您不能通过表单使用 HTTP DELETE,因此您必须进行 AJAX 调用和/或通过 POST 隧道 DELETE。

于 2010-06-19T21:34:39.063 回答
12

我实际上是在寻找一个答案,而不是提供一个答案——但是在 REST 上下文中,“reset_password”对我来说听起来是错误的,因为它是一个动词,而不是一个名词。即使你说你正在做一个“重置动作”名词——使用这个理由,所有动词都是名词。

此外,搜索相同答案的人可能没有想到您可以通过安全上下文获取用户名,而根本不必通过 url 或正文发送它,这让我感到紧张。

于 2014-01-06T20:24:10.227 回答
12

通常,您不想在初始请求中删除或销毁用户的现有密码,因为这可能是由无权访问电子邮件的用户(无意或有意)触发的。相反,更新用户记录上的重置密码令牌并将其发送到电子邮件中包含的链接中。单击该链接将确认用户收到令牌并希望更新其密码。理想情况下,这也是时间敏感的。

在这种情况下,RESTful 操作将是一个 POST:触发 PasswordResets 控制器上的创建操作。该操作本身将更新令牌并发送电子邮件。

于 2013-01-28T20:59:18.797 回答
8

我认为更好的主意是:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

关于提供数据:

  • 重置当前密码

    • 电子邮件应在正文中给出。
  • 创建新密码(重置后)

    • 新密码、激活码和 emailID 应在正文中给出。
  • 更新密码(用于登录用户)

    • 旧密码,新密码应在正文中提及。
    • 参数中的用户 ID。
    • 标头中的身份验证令牌。
于 2017-11-24T14:25:58.123 回答
5

有几个考虑因素:

密码重置不是幂等的

密码更改会影响用作执行密码的凭据的数据,因此如果在存储的凭据已更改时简单地逐字重复请求,则可能会使以后的尝试无效。例如,如果使用临时重置令牌来允许更改,这在忘记密码的情况下是习惯性的,那么该令牌应该在成功更改密码后过期,这再次使复制请求的进一步尝试无效。因此,更改密码的 RESTful 方法似乎POSTPUT.

数据加载中的 ID 或电子邮件可能是多余的

虽然这不违反 REST 并且可能有一些特殊目的,但通常不需要指定 ID 或电子邮件地址来重置密码。想一想,为什么要将电子邮件地址作为数据的一部分提供给应该以一种或另一种方式通过身份验证的请求?如果用户只是更改密码,他们需要进行身份验证(通过用户名:密码、电子邮件:密码或通过标头提供的访问令牌)。因此,我们可以从该步骤访问他们的帐户。如果他们忘记了密码,他们将获得一个临时重置令牌(通过电子邮件),他们可以将其专门用作执行更改的凭据。在这种情况下,通过令牌进行身份验证应该足以识别他们的帐户。

考虑到以上所有因素,我认为这是更改 RESTful 密码的正确方案:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}
于 2018-06-23T13:11:40.890 回答
2

如果您决定使用 /users/{id}/password 方法,我不会更改密码并向他们发送新密码,并坚持您的想法,即请求是它自己的资源。即 /user-password-request/ 是资源,使用 PUT,用户信息应该在正文中。不过,我不会更改密码,我会向用户发送一封电子邮件,其中包含指向包含 request_guid 的页面的链接,该页面可以与对 POST /user/{id}/password/?request_guid= 的请求一起传递xxxxx

这将更改密码,并且不允许有人通过请求更改密码来骚扰用户。

此外,如果有未完成的请求,初始 PUT 可能会失败。

于 2014-09-03T02:51:50.203 回答
0

我们更新登录用户密码 PUT /v1/users/password - 使用 AccessToken 识别用户 ID。

交换用户 ID 是不安全的。Restful API 必须使用 HTTP 标头中收到的 AccessToken 来识别用户。

spring-boot 中的示例

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
于 2018-02-28T15:38:33.157 回答