通过 REST API 验证/更改密码
Posted
技术标签:
【中文标题】通过 REST API 验证/更改密码【英文标题】:Validate/Change Password via REST API 【发布时间】:2011-11-22 17:58:27 【问题描述】:我想通过 REST API 更改用户密码。这不是忘记或重置密码功能,而是登录用户想要更改密码。
该表单需要当前密码、新密码和对新密码的确认。但是,我想在用户填写时验证每个表单字段。这对于newPassword
和confirmNewPassword
(客户端)来说是微不足道的,但对于currentPassword
来说却不是。目前正在通过PUT /users/:id
更新用户对象。如果传递了密码参数,我会检查currentPassword
参数并确保在保存之前它是正确的。但是,为了验证,我不确定最好的方法。
我也有一个POST /users/validate
- 也不确定这是否是最好的。这将验证用户对象的创建和更新,但仅验证属于用户对象的字段(email
、username
、password
)。 currentPassword
不是其中之一。想知道如何处理这个问题。我考虑过的一些事情:
POST /users/check_password
,
POST /users/validate
(如果传递了该参数,则添加对 currentPassword 的验证,并检查 currentPassword 是否与用户现有密码匹配)和
POST /users/:id/validate
(现有用户单独验证,需要currentPassword
)。
任何想法或建议将不胜感激。我的第一个仅通过 REST API 公开功能的应用程序。
【问题讨论】:
【参考方案1】:我首先要指出,身份验证通常是在 REST 模型之外处理的。当用户提供他们的凭据时,他们没有提供他们帐户对象的状态 (REST) 的表示;他们收到的回应也不是这样的表示。由于用户帐户资源状态不包括“当前”和“新”密码,因此在请求中提供“当前”和“新”密码永远无法真正适合 REST 模型,但专业人士经常描述RESTful 的“连续统一体”,一些 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 不必是幂等的。
【讨论】:
您说 PUT 是幂等的,因为“在第一次请求之后,用户的密码是'b',而在第二次请求之后,用户的密码是'b'”,尽管有实际的响应。如果在 2 个 PUT 之间,另一个客户端执行更改密码的调用,则情况并非如此。在这种情况下,两个初始 PUT 不是幂等的。 @nimiq:我是这样理解你的场景的。假设我的密码是“a”: 1. 我发出将密码从“a”更改为“b”的请求 2. 另一个客户发出将密码从“b”更改为“c”的请求 3. 我重新-发出我将密码从“a”更改为“b”的请求,那么在请求(1)之后我的密码是“b”是正确的,但在请求(3)之后不是。但是请求仍然是幂等的,因为如果我发出相同的请求一次、两次或 n 次在一个完整的行中,那么效果是相同的。但同样,您可能想要一个非幂等的密码更改请求。见上文。 恕我直言,PATCH 是技术上最正确的方法。 POST 用于创建“从属”资源,这绝对不是这里发生的事情。理论上,PUT 是覆盖整个实体;再次,不是发生了什么。至于幂等性,我认为这仅在请求成功的情况下才相关。因此,如果随后的相同请求由于密码更改而失败,则应使用“403 Forbidden”拒绝该请求。我的 $.02。【参考方案2】:我不喜欢 /check_password 或 /validate 因为它们是动词;您的第一个“更新用户”是更好的 REST。
您可以将 currentPassword 作为非持久字段添加到您的用户对象,或作为身份验证标头的一部分(用户名:密码)。
不过,我肯定会将其从 PUT 更改为 POST,因为使用相同 currentPassword 的相同调用不能成功两次(PUT 是幂等的)。
【讨论】:
【参考方案3】:您正在更改用户资源的属性(即密码)。如果您使用 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(如果无效)来满足)。
【讨论】:
【参考方案4】:另一种选择是在user
上创建代理资源。如果您使用 HATEOAS,您可以从 user
资源链接到 user/x/pwdchange
。我想澄清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 视图。
【讨论】:
【参考方案5】:您可能会想到为什么需要在输入当前密码后立即对其进行验证。我还没有看到一个网站这样做。其次,拥有一个只是验证某些东西的服务是非常好的。它被称为实用的诗句打败自己试图成为 RESTFul
【讨论】:
您验证当前密码以防止欺骗攻击(经过身份验证的用户尝试更改具有更多权限的用户的密码)。以上是关于通过 REST API 验证/更改密码的主要内容,如果未能解决你的问题,请参考以下文章
如何从 REST API(PUT 方法)更新 Keycloak 密码?