2011-11-22 203 views
4

我想通过REST API更改用户密码。这不是一个遗忘或重置密码功能,而是一个登录用户想要更改密码。通过REST API验证/更改密码

该表格需要当前密码,新密码和新密码确认。但是,我想验证每个表单字段,因为用户填写它。这对于newPasswordconfirmNewPassword(客户端)而言是微不足道的,但对于currentPassword则不是。目前通过PUT /users/:id执行对用户对象的更新。如果通过密码参数,我检查currentPassword参数,并确保在保存前它是正确的。但是,对于验证,我不确定最佳方法。

我也有一个POST /users/validate - 不知道这是否是最好的。这将验证用户对象的创建和更新,但仅验证属于用户对象的字段(emailusernamepassword)。 currentPassword不是其中之一。想知道如何处理这个。有些事情我已经考虑:

POST /users/check_passwordPOST /users/validate(添加在验证了currentPassword如果参数传递,并检查currentPassword用户现有密码匹配)和 POST /users/:id/validate(对于现有用户单独的验证,需要currentPassword) 。

任何想法或建议将不胜感激。我的第一个应用程序只通过REST API公开功能。

回答

1

您可能会考虑为什么您输入密码时需要验证当前密码。我还没有看到一个网站这样做。其次,拥有仅仅验证某些东西的服务是完全正确的。它被称为是实用的经文跳动自己试图成为RESTFul

+0

验证当前密码以防止恶意攻击(经过身份验证的用户尝试更改具有更多权限的用户的密码)。 – orbfish

6

我不喜欢/ check_password或/验证,因为它们是动词;你的第一个“更新用户”更好的REST。

您可以将当前密码添加到您的用户对象中,作为未被执行的字段,或作为验证标头(用户名:密码)的一部分。

但是我肯定会把它从PUT改为POST,因为使用同一个currentPassword的同一个调用不能成功两次(PUT是幂等的)。

7

我将首先指出认证通常是在REST模型之外处理的。当用户提供他们的凭证时,他们没有提供其帐户对象的状态(REST)的重新呈现;他们也没有收到这样的表示。由于用户帐户资源状态不包含“当前”和“新”密码,因此在请求中提供“当前”和“新”密码永远不能真正适合REST模型,但专业人员通常会描述RESTfulness'continuum',其中一些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次连续,服务器在 th请求的影响下的状态将与服务器在第一个请求的影响下的状态相同。用户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不需要是幂等的。

+1

你说PUT是幂等的,因为“第一次请求用户的密码是'b',第二次请求后用户的密码是'b'”,尽管有实际的响应。在两个PUT之间的情况下,另一个客户端执行呼叫以更改密码时,情况并非如此。在这种情况下,2个初始PUT不是幂等的。 – nimiq

+0

@nimiq:以下是我理解你的场景的方式。假设我的密码为'a': 1.我发出将密码从'a'更改为'b'的请求 2.另一客户发出将密码从'b'更改为'c'的请求 3 。我重新发出我的请求,将密码从'a'更改为'b' 然后您的请求(1)后密码为'b',但请求(3)后密码不正确。 但是这个请求仍然是幂等的,因为如果我在一个不间断的行中发出相同的请求一次,两次或n次*,那么效果是相同的。 但是,您可能还想要一个不幂等的密码更改请求。往上看。 – JASchilz

+0

恕我直言,补丁是技术上最正确的方法。 POST用于创建“从属”资源,这绝对不是这里发生的事情。从理论上讲,PUT是完全覆盖一个实体的;再次,不是发生了什么事情。至于幂等性,我认为这只在请求成功的情况下才有意义。因此,如果随后的相同请求由于密码被改变而失败,则该请求应该被'403 Forbidden'拒绝。我的$ .02。 – broofa

1

您正在更改用户资源的属性(即密码)。如果您使用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(如果无效)。

2

另一种选择是在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观点完全兼容问题领域。