RESTful 密码重置
构建用于重置密码的 RESTful 资源的正确方法是什么?
此资源旨在为丢失或忘记密码的人提供密码重置器。它会使他们的旧密码失效,并通过电子邮件向他们发送密码。
我有两个选项:
POST /reset_password/{user_name}
或者...
POST /reset_password
-Username passed through request body
我很确定请求应该是 POST。我对自己选择的名字不太有信心。我不确定 user_name 是否应该通过 URL 或请求正文传递。
What is the proper way to structure a RESTful resource for resetting a password?
This resource is meant to be a password resetter for someone who has lost or forgotten their password. It invalidates their old password and e-mails them a password.
The two options that I have are:
POST /reset_password/{user_name}
or...
POST /reset_password
-Username passed through request body
I'm pretty sure the request should be a POST. I'm less confident that I have selected an appropriate name. And I'm not sure if the user_name should be passed through the URL or the request body.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
未经身份验证的用户
我们在
api/v1/account/password
端点上执行PUT
请求,并需要一个带有相应帐户电子邮件的参数来标识用户想要访问的帐户重置(更新)密码:注意:正如@DougDomeny在他的评论中提到的那样,将电子邮件作为网址中的查询字符串传递是存在安全风险的。使用
https
时不会公开 GET 参数(对于此类请求,您应该始终使用正确的https
连接),但还涉及其他安全风险。您可以在 中阅读有关此主题的更多信息这篇博文在这里。在请求正文中传递电子邮件将是比将其作为 GET 参数传递更安全的替代方案:
请求正文:
响应有一个
202
接受 响应含义:用户将收到一封电子邮件:
[电子邮件受保护] 并处理更新请求取决于对电子邮件中的链接采取的操作。
打开此电子邮件中的链接将定向到前端应用程序上的重置密码表单,该表单使用链接中的重置密码令牌作为隐藏输入字段的输入(该令牌是作为查询字符串的链接的一部分)。另一个输入字段允许用户设置新密码。用于确认新密码的第二个输入将用于前端验证(以防止拼写错误)。
注意: 在电子邮件中,我们还可以提到,如果用户没有初始化任何密码重置,他/她可以忽略该电子邮件并继续使用他/她当前的密码正常使用该应用程序
当使用新密码和令牌作为输入提交表单时,将发生重置密码过程。表单数据将再次通过
PUT
请求发送,但这次包括令牌,我们将用新值替换资源密码:请求正文:
响应将是
204
无内容响应经过身份验证的用户
对于想要更改密码的经过身份验证的用户,可以立即执行
PUT
请求,而无需电子邮件(服务器知道我们正在更新密码的帐户)。在这种情况下,表单将提交两个字段:请求正文:
Unauthenticated users
We do a
PUT
request on aapi/v1/account/password
endpoint and require a parameter with the corresponding account email to identify the account for which the user wants to reset (update) the password:Note: As @DougDomeny mentioned in his comment passing the email as a query string in the url is a security risk. GET parameters are not exposed when using
https
(and you should always use a properhttps
connection for such requests) but there are other security risks involved. You can read more on this topic in this blog post here.Passing the email in the request body would be a more secure alternative to passing it as a GET param:
Request body:
The response has a
202
accepted response meaning:The user will receive an email at
[email protected]
and processing the update request depends on actions taken with the link from the email.Opening the link from this email will direct to a reset password form on the front end application that uses the reset password token from the link as input for a hidden input field (the token is part of the link as a query string). Another input field allows the user to set a new password. A second input to confirm the new password will be used for validation on front-end (to prevent typos).
Note: In the email we could also mention that in case the user didn't initialize any password reset he/she can ignore the email and keep using the application normally with his/her current password
When the form is submitted with the new password and the token as inputs the reset password process will take place. The form data will be sent with a
PUT
request again but this time including the token and we will replace the resource password with a new value:Request body :
The response will be a
204
no content responseAuthenticated users
For authenticated users that want to change their password the
PUT
request can be performed immediately without the email (the account for which we are updating the password is known to the server). In such case the form will submit two fields:Request body:
更新:(下面进一步评论)
我会选择这样的内容:
您有一个用户集合,其中单个用户由
{user_name}
指定。然后,您可以指定要操作的操作,在本例中为reset_password
。这就像说“为{user_name}
创建 (POST
) 一个新的reset_password
操作”。以前的答案:
我会采用这样的方法:
您将有两个集合,一个用户集合和每个用户的属性集合。用户由
:user_id
指定,属性由password
指定。PUT
操作更新集合的寻址成员。UPDATE: (further to comment below)
I would go for something like this:
You have a collection of users, where the single user is specified by the
{user_name}
. You would then specify the action to operate on, which in this case isreset_password
. It is like saying "Create (POST
) a newreset_password
action for{user_name}
".Previous answer:
I would go for something like this:
You'd have two collections, a users collection, and an attributes collection for each user. The user is specified by the
:user_id
and the attribute is specified bypassword
. ThePUT
operation updates the addressed member of the collection.让我们先来一下超级 RESTful 吧。为什么不使用密码的 DELETE 操作来触发重置?有道理,不是吗?毕竟,您实际上是在放弃现有密码,转而使用另一个密码。
这意味着你会这样做:
现在,有两个重要的警告:
HTTP DELETE 应该是幂等的(一个奇特的词,表示“多次执行也没什么大不了”)。如果您正在执行标准操作,例如发送“密码重置”电子邮件,那么您将会遇到问题。您可以使用布尔值“Is Reset”标志来解决此问题,标记用户/密码。每次删除时,您都会检查此标志;如果未设置那么您可以重置密码并发送电子邮件。 (请注意,拥有此标志也可能有其他用途。)
您无法通过表单使用 HTTP DELETE,因此您必须进行 AJAX 调用和/或通过 POST 隧道传输 DELETE。
Let's get uber-RESTful for a second. Why not use the DELETE action for the password to trigger a reset? Makes sense, doesn't it? After all, you're effectively discarding the existing password in favor of another one.
That means you'd do:
Now, two big caveats:
HTTP DELETE is supposed to be idempotent (a fancy word for saying "no big deal if you do it multiple times"). If you're doing the standard stuff like sending out a "Password Reset" email, then you're going to run into problems. You could work around this tagging the user/password with a boolean "Is Reset" flag. On every delete, you check this flag; if it's not set then you can reset the password and send your email. (Note that having this flag might have other uses too.)
You can't use HTTP DELETE through a form, so you'll have to make an AJAX call and/or tunnel the DELETE through the POST.
通常,您不想在初始请求时删除或销毁用户的现有密码,因为这可能是由无权访问电子邮件的用户触发的(无意或有意)。相反,请更新用户记录上的重置密码令牌,并将其通过电子邮件中包含的链接发送。单击该链接将确认用户收到令牌并希望更新其密码。理想情况下,这也对时间敏感。
在这种情况下,RESTful 操作将是 POST:触发 PasswordResets 控制器上的创建操作。该操作本身将更新令牌并发送电子邮件。
Often you don't want to delete or destroy the user's existing password on the initial request, as this may have been triggered (unintentionally or intentionally) by a user that does not have access to the email. Instead, update a reset password token on the user record and send that in a link included in an email. Clicking on the link would confirm the user received the token and wished to update their password. Ideally, this would be time sensitive as well.
The RESTful action in this case would be a POST: triggering the create action on the PasswordResets controller. The action itself would update the token and send an email.
我实际上是在寻找答案,并不是要提供答案 - 但在 REST 上下文中“reset_password”对我来说听起来是错误的,因为它是一个动词,而不是名词。即使你说你正在做一个“重置动作”名词 - 使用这个理由,所有动词都是名词。
另外,对于搜索相同答案的人来说,可能没有想到您可以通过安全上下文获取用户名,而根本不必通过 url 或正文发送它,这让我感到紧张。
I'm actually looking for an answer, not meaning to provide one - but "reset_password" sounds wrong to me in a REST context because it's a verb, not a noun. Even if you say you're doing a "reset action" noun - using this justification, all verbs are nouns.
Also, it may not have occurred to someone searching for the same answer that you may be able to get the username through the security context, and not have to send it through the url or the body at all, which makes me nervous.
需要考虑以下几点:
密码重置不是幂等
的 密码更改会影响用作执行密码的凭据的数据,因此,如果在存储的凭据已更改时简单地逐字重复请求,则可能会使将来的尝试无效。例如,如果使用临时重置令牌来允许更改(在忘记密码的情况下通常如此),则该令牌应在成功更改密码后过期,这再次使复制请求的进一步尝试无效。因此,采用 RESTful 方式更改密码似乎比
PUT
更适合POST
。数据加载中的 ID 或电子邮件可能是多余的
尽管这并不违反 REST 并且可能有一些特殊目的,但通常没有必要为密码重置指定 ID 或电子邮件地址。想一想,为什么要将电子邮件地址作为数据的一部分提供给应该通过某种方式进行身份验证的请求?如果用户只是更改密码,则需要进行身份验证(通过用户名:密码、电子邮件:密码或通过标头提供的访问令牌)。因此,我们可以从该步骤访问他们的帐户。如果他们忘记了密码,他们将获得一个临时重置令牌(通过电子邮件),他们可以专门将其用作执行更改的凭据。在这种情况下,通过令牌进行身份验证应该足以识别他们的帐户。
考虑到上述所有因素,我认为这是 RESTful 密码更改的正确方案:
There are a few considerations to take:
Password resets are not idempotent
A password change affects the data used as credentials to perform it, which as a result could invalidate future attempts if the request is simply repeated verbatim while the stored credentials have changed. For instance, if a temporary reset token is used to allow the change, as it is customary in a forgotten password situation, that token should be expired upon successful password change, which again nullifies further attempts at replicating the request. Thus a RESTful approach to a password change seems to be a job better suited for
POST
thanPUT
.ID or e-mail in the data load is probably redundant
Although that's not against REST and may have some special purpose, it is often unnecessary to specify an ID or email address for a password reset. Think about it, why would you provide the email address as part of the data to a request that is supposed to go through authentication one way or another? If the user is simply changing their password they need to authenticate in order to do so (via username:password, email:password, or access token provided via headers). Hence, we have access to their account from that step. If they had forgotten their password, they would've been provided with a temporary reset token (via email) that they can use specifically as credentials to perform the change. And in this case authentication via token should be enough to identify their account.
Taking all of the above into consideration here's what I believe to be the proper scheme to a RESTful password change:
我认为更好的想法是:
关于提供数据:
重置当前密码
创建新密码(重置后)
更新密码(登录用户)
I think better idea would be:
Regarding supplying the data:
To reset current password
To create new password (after reset)
To update the password (for loggedIn user)
如果您决定使用 /users/{id}/password 方法,并坚持您的想法,即请求本身就是一种资源,那么我不会有任何更改密码并向他们发送新密码的方法。即 /user-password-request/ 是资源,并且使用 PUT,用户信息应该在正文中。
不过,我不会更改密码,我会向用户发送一封电子邮件,其中包含指向包含 request_guid 的页面的链接,该页面可以与 POST /user/{id}/password/?request_guid= 的请求一起传递xxxxx
这会更改密码,并且不允许有人通过请求更改密码来控制用户。
另外,如果存在未完成的请求,初始 PUT 可能会失败。
I wouldn't have something that changes the password and send them a new one if you decide to use the /users/{id}/password method, and stick to your idea that the request is a resource of its own. ie /user-password-request/ is the resource, and is use PUT, user info should be in the body.
I wouldn't change the password though, Id send an email to the user which contains a link to a page which contains a request_guid, which could be passed along with a request to POST /user/{id}/password/?request_guid=xxxxx
That would change the password, and it doesn't allow someone to hose a user by requesting a password change.
Plus the initial PUT could fail if there is an outstanding request.
如果您担心安全性并且不想在 URL 中公开用户的标识符(例如,user_name),您可以考虑另一种方法:
使用唯一的令牌来标识密码重置请求,而不是用户的标识符。当发起密码重置请求时,应生成此令牌并将其安全地存储在服务器上。
向用户发送包含令牌作为 URL 参数的链接的电子邮件,例如:
当用户单击链接时,客户端会向服务器发送 GET 请求,其中 URL 参数中包含令牌。
服务器验证令牌并返回一个页面,用户可以在其中输入新密码。
当用户提交新密码时,客户端向服务器发送 POST 请求,并在请求正文中包含新密码和令牌。
服务器验证令牌并更新用户的密码。
此方法的优点是不会在 URL 中暴露用户的标识符,这出于安全原因很有用。但是,它需要额外的步骤来生成安全令牌并将其存储在服务器上,并在用户提交新密码时验证服务器上的令牌。
If you are concerned about security and prefer not to expose the user's identifier (e.g., user_name) in the URL, you could consider an alternative approach:
Use a unique token to identify the password reset request, rather than the user's identifier. This token should be generated and stored securely on the server when the password reset request is initiated.
Send an email to the user with a link that includes the token as a URL parameter, for example:
When the user clicks the link, the client sends a GET request to the server with the token in the URL parameter.
The server validates the token and returns a page where the user can enter a new password.
When the user submits the new password, the client sends a POST request to the server with the new password and the token in the request body.
The server validates the token and updates the user's password.
This approach has the advantage of not exposing the user's identifier in the URL, which can be useful for security reasons. However, it requires additional steps to generate and store a secure token on the server, and to validate the token on the server when the user submits the new password.
我们更新登录用户密码
PUT /v1/users/password - 使用 AccessToken 识别用户 ID。
交换用户 ID 并不安全。 Restful API 必须使用 HTTP 标头中收到的 AccessToken 来识别用户。
spring-boot 中的示例
We Update logged user Password
PUT /v1/users/password - identify the user id using AccessToken.
It is not secure to exchange user id. The Restful API must identify the user using AccessToken received in HTTP header.
Example in spring-boot