RESTful 密码重置

为重置密码构造 RESTful 资源的正确方法是什么?

这个资源是为那些丢失或忘记密码的人提供的一个密码重置程序。它使他们的旧密码无效,并通过电子邮件向他们发送密码。

我有两个选择:

POST /reset_password/{user_name}

或者..。

POST /reset_password
-Username passed through request body

我很确定这个请求应该是一个 POST。我不太确定我选了一个合适的名字。而且我不确定 user _ name 是否应该通过 URL 或请求主体传递。

105449 次浏览

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

我会选择这样的:

POST /users/:user_id/reset_password

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


回答:

我会选择这样的:

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

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

让我们用一下 Uber-REST。为什么不使用 DELETE 操作作为密码来触发重置?有道理,不是吗?毕竟,您实际上是在放弃现有的密码,而使用另一个密码。

这意味着你会做:

DELETE /users/{user_name}/password

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

  1. HTTP DELETE 被认为是幂等的 (一个花哨的词语,意思是“如果多次执行也没什么大不了的”)。如果你正在做一些标准的事情,比如发送一封“密码重置”的电子邮件,那么你就会遇到问题。您可以使用一个布尔“ Is Reset”标志来标记用户/密码。在每次删除时,检查这个标志; 如果没有设置 那么,你可以重置密码并发送电子邮件。(注意,使用这个标志可能还有其他用途。)

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

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

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

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

而且,当有人在搜索同一个答案时,他可能没有想到,你可以通过安全上下文获得用户名,而不必通过 URL 或主体发送,这让我感到紧张。

如果您决定使用/users/{ id }/password 方法,并坚持认为请求是其自身的资源,那么我不会修改密码并向他们发送一个新的密码。即/user-password-request/是资源,使用 PUT 时,用户信息应该在主体中。 我不会改变密码,但 ID 发送一封电子邮件给用户,其中包含一个链接到一个页面,其中包含一个 request _ guid,这可以与一个请求一起传递到 POST/user/{ Id }/password/?Request _ guid = xxxxx

这将改变密码,并且它不允许有人通过请求更改密码来消灭用户。

另外,如果有未处理的请求,初始 PUT 可能会失败。

未经身份验证的用户

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

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

注: 正如“ strong >@DougDomeny 在他的评论中提到的,在 URL 中将电子邮件作为查询字符串传递是一种安全风险。在使用 https时,GET 参数不会公开(对于这种请求,您应该始终使用适当的 https连接) ,但是还有其他安全风险。你可以在这里阅读更多关于这个主题的文章。

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

PUT : /api/v1/account/password

请求机构:

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

该响应具有 接受 202响应意味着:

请求已被接受处理,但处理尚未完成。请求最终可能会被执行,也可能不会,因为在实际进行处理时可能会被禁止。没有从这样的异步操作重新发送状态代码的工具。

用户将在 email@example.com收到一封电子邮件,处理更新请求取决于从电子邮件链接采取的行动。

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

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

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

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

PUT : /api/v1/account/password

请求机构:

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

响应将是 没有内容响应

服务器已经完成了请求,但是不需要返回实体主体,并且可能希望返回更新的元信息。响应可能包括新的或更新的元信息的实体标题的形式,如果出现应该与所请求的变体。

身份验证用户

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

PUT : /api/v1/account/password

请求机构:

{
"old":"password",
"new":"password"
}

我觉得更好的办法是:

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)

关于提供数据:

  • 重置当前密码

    • 电子邮件应以正文形式发出。
  • 创建新密码(重置后)

    • 新的密码、激活码和电子邮件 ID 应在正文中提供。
  • 更新密码(对于 loggedIn 用户)

    • 旧密码,新密码应在正文中提及。
    • Params 的用户名。
    • 标题中的 Auth Token。

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

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

例如在弹簧启动

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}

需要考虑以下几点:

密码重置不等幂

密码更改会影响用作执行密码更改的凭据的数据,因此,如果在更改存储凭据的同时只是重复请求,那么以后的尝试可能会失效。例如,如果使用临时重置令牌来允许更改(在忘记密码的情况下通常是这样) ,那么在成功更改密码时,该令牌应该过期,这将再次使进一步复制请求的尝试失效。因此,RESTful 密码更改方法似乎比 PUT更适合于 POST

数据加载中的 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"}