构建RESTful资源以重置密码的正确方法是什么?
此资源旨在为丢失或忘记密码的人设置密码。它使旧密码无效,并通过电子邮件向他们发送密码。
我有两个选择:
POST /reset_password/{user_name}
...或
POST /reset_password
-Username passed through request body
我很确定请求应该是POST。我没有信心选择一个合适的名字。而且我不确定是否应该通过URL或请求正文传递user_name。
答案 0 :(得分:56)
我们在PUT
端点上发出api/v1/account/password
请求,并要求使用相应帐户电子邮件的参数来标识用户要重置(更新)密码的帐户:
PUT : /api/v1/account/password?email={email@example.com}
注意: 在评论中提及 @DougDomeny 将电子邮件作为网址中的查询字符串传递是一种安全风险。使用https
时,不会公开GET参数(并且您应始终对此类请求使用正确的https
连接),但还存在其他安全风险。您可以在this blog post here。
在请求正文中传递电子邮件是将其作为GET参数传递的更安全的替代方法:
PUT : /api/v1/account/password
请求正文:
{
"email": "email@example.com"
}
回复的响应有 202
accepted :
请求已被接受处理,但处理尚未完成。该请求最终可能会或可能不会被执行,因为在实际处理时可能不允许该请求。没有从异步操作中重新发送状态代码的工具。
用户将收到email@example.com
处的电子邮件,处理更新请求取决于使用电子邮件中的链接执行的操作。
https://example.com/password-reset?token=1234567890
打开此电子邮件中的链接将指向前端应用程序上的重置密码表单,该表单使用链接中的重置密码令牌作为隐藏输入字段的输入(该令牌是链接的一部分作为查询字符串) 。另一个输入字段允许用户设置新密码。确认新密码的第二个输入将用于前端验证(以防止拼写错误)。
注意: 在电子邮件中我们还可以提到,如果用户没有初始化任何密码重置,他/她可以忽略该电子邮件并继续正常使用该应用程序与他/她目前的密码
当使用新密码和令牌作为输入提交表单时,将执行重置密码过程。表单数据将再次发送PUT
请求,但这次包括令牌,我们将使用新值替换资源密码:
PUT : /api/v1/account/password
请求正文:
{
"token":"1234567890",
"new":"password"
}
回复将是 204
no content 回复
服务器已完成请求但不需要返回实体主体,并且可能希望返回更新的元信息。响应可以包括实体标题形式的新的或更新的元信息,如果存在,应该与所请求的变体相关联。
对于想要更改密码的经过身份验证的用户,可以在没有电子邮件的情况下立即执行PUT
请求(服务器已知我们正在为其更新密码的帐户)。在这种情况下,表单将提交两个字段:
PUT : /api/v1/account/password
请求正文:
{
"old":"password",
"new":"password"
}
答案 1 :(得分:48)
更新(进一步在下面发表评论)
我会选择这样的事情:
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
操作更新集合的已寻址成员。
答案 2 :(得分:17)
让我们在一秒钟内获得超级RESTful。为什么不使用DELETE操作来触发重置?有道理,不是吗?毕竟,您实际上是在放弃现有密码而不是另一个密码。
这意味着你会这样做:
DELETE /users/{user_name}/password
现在,有两大注意事项:
HTTP DELETE is supposed to be idempotent(一句好话,说“如果多次这样做就没什么大不了的”)。如果您正在执行发送“密码重置”电子邮件等标准内容,那么您将遇到问题。您可以使用布尔值“Is Reset”标记来解决此标记用户/密码的问题。在每次删除时,都会检查此标志;如果未设置然后,您可以重置密码并发送电子邮件。 (请注意,拥有此标志也可能有其他用途。)
You can't use HTTP DELETE through a form,因此您必须通过POST进行AJAX调用和/或隧道传输DELETE。
答案 3 :(得分:11)
通常,您不希望在初始请求中删除或销毁用户的现有密码,因为这可能是由无权访问该电子邮件的用户(无意或有意)触发的。而是更新用户记录上的重置密码令牌,并将其发送到电子邮件中包含的链接中。单击该链接将确认用户收到了令牌并希望更新其密码。理想情况下,这也是时间敏感的。
在这种情况下,RESTful操作将是POST:在PasswordResets控制器上触发创建操作。该操作本身会更新令牌并发送电子邮件。
答案 4 :(得分:8)
我实际上是在寻找一个答案,而不是提供答案 - 但在REST环境中,“reset_password”听起来不对,因为它是动词,而不是名词。即使你说你正在做一个“重置动作”名词 - 使用这个理由,所有动词都是名词。
此外,搜索相同答案的人可能没有想到您可以通过安全上下文获取用户名,而不必通过网址或正文发送,这让我感到紧张
答案 5 :(得分:5)
我认为更好的主意是:
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)
关于提供数据:
重设当前密码
创建新密码(重置后)
更新密码(对于loggedIn用户)
答案 6 :(得分:3)
有一些注意事项:
密码更改会影响用作凭据的数据,因此,如果在存储的凭据已更改的情况下简单逐字重复请求,则可能会使以后的尝试无效。例如,如果使用临时重置令牌来允许更改(如在忘记密码的情况下所惯用的那样),则该令牌应在成功更改密码后过期,这再次使复制请求的进一步尝试无效。因此,使用RESTful方式更改密码似乎比con.sqlquery("select PO_No from TBL_PO where PO_No not in (select PO_No from GRN)");
更适合POST
。
尽管这不是针对REST的,并且可能具有某些特殊用途,但通常不必指定ID或电子邮件地址来重置密码。考虑一下,您为什么要向应该以某种方式通过身份验证的请求提供电子邮件地址作为数据的一部分?如果用户只是更改密码,则需要进行身份验证(通过用户名:密码,电子邮件:密码或通过标头提供的访问令牌)。因此,我们可以从该步骤访问他们的帐户。如果他们忘记了密码,则会为他们提供一个临时重置令牌(通过电子邮件),可以将它们专门用作执行更改的凭据。在这种情况下,通过令牌进行身份验证就足以识别其帐户。
考虑到上述所有因素,以下是我认为是RESTful密码更改的适当方案:
PUT
答案 7 :(得分:2)
如果您决定使用/ users / {id} / password方法,我不会更改密码并发送新密码,并坚持认为请求是自己的资源。 ie / user-password-request /是资源,并且使用PUT,用户信息应该在正文中。 我不会更改密码,Id会向用户发送一封电子邮件,其中包含指向包含request_guid的页面的链接,该页面可以与POST / user / {id} / password /?request_guid =一起传递。 XXXXX
这会改变密码,并且不允许有人通过请求更改密码来阻止用户。
如果有未完成的请求,则初始PUT可能会失败。
答案 8 :(得分: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 */
}