2015-12-08 107 views
1

目标:使用RESTful服务安全且安全地更新用户密码。RESTful更新密码

我有点困惑的工作流程,采用最佳实践,应该是什么:
    1)更新了谁知道有现有的密码,用户密码
    2)设密码如果用户忘记了。
    3.)URIs(资源)是RESTful吗?为了这个Web应用程序的目的,我只需要GET和POST进行更改。

我相信我的代码可能是多余的。密码更新方法(如下所示)未正确更新密码。它正在改变它,但是当我尝试使用在new_password中设置的新密码登录时,密码不匹配。我同样按照Michael Merickel的这个STACKs answer来更新。

用户= DBSession.query(用户).filter_by(电子邮件=电子邮件)。首先() 如果用户: user.password的= NEW_PASSWORD

谢谢你的任何见解/输入。我是新手,想要正确编码。

所有这一切都是通过HTML而不是JSON。
软件:Python的2.7.9,1.5.7金字塔,SQLAlchemy的1.0.9


资源: __init__.py

config.add_route('users', '/users') 
config.add_route('user', '/users/{id:\d+}/{login}') #/{login} added login 

config.add_route('reset_password', '/users/{username}/reset_password')#reset 
config.add_route('new_password', '/users/{username}/new_password')#new 
config.add_route('save_password', '/save_password')#new 

views.py

#http://0.0.0.0:6432/users/dorisday/reset_password <--like this 
@view_config(route_name='reset_password', renderer='templates/password_recovery.jinja2') 
def forgot_password(request): 
    if request.method == 'GET': 
     username = request.params['username'] 
     if username is not None: 
      user = api.retrieve_user(username) 

     return {} 

#http://0.0.0.0:6432/users/macycat/new_password <--like this 
@view_config(route_name='new_password', request_method='GET', renderer='templates/new_password.jinja2') 
def new_password(request): 
    logged_in_userid = authenticated_userid(request) 
    if logged_in_userid is None: 
     raise HTTPForbidden() 
    user = api.retrieve_user(logged_in_userid) 
    return {'user': user.username} 

@view_config(route_name='save_password', request_method='POST', renderer='templates/new_password.jinja2') 
def save_password(request): 
    with transaction.manager: 
     logged_in_userid = authenticated_userid(request) 
     if logged_in_userid is None: 
      raise HTTPForbidden() 
     user = api.retrieve_user(logged_in_userid) 
     if 'form.submitted' in request.params: 
      password = request.params['old_password'] 
      new_password = request.params['new_password'] 
      confirm_password = request.params['confirm_password'] 
      if new_password == confirm_password: 
       continue 
       if user is not None and user.validate_password(password): #encrypted way of checking password from DB 
        user.password = new_password 

       message = 'Whoops! Passwords do not match.' 

     transaction.commit() 
     raise HTTPSeeOther(location=request.route_url('login')) 
     return {'message': message, 'new_password': new_password} 

散列密码在SQLALCHEMY DB方案中,如下所示:

Storing and validating encrypted password for login in Pyramid

+0

你应该使用一些认证(OAuth的认证可以和它依然宁静)担心这个......你应该也没有实际更改密码,但通过电子邮件发送相关电子邮件帐户链接到“更改密码”类型的东西 –

+0

@JoranBeasley我正在使用金字塔的身份验证代码进行登录。至于重置密码,我不知道该怎么做。你能指出我的东西是不是Django,因为我使用SQLAlchemy和Pyramid。也许例如伪代码?作为蓝图,这将非常有帮助。还是链接?谢谢!!! – thesayhey

回答

6

1.更新密码。

通常情况下,密码存储在散列形式的数据库,因此,如果有人偷了你的数据库,他们不能轻易得到密码。从你的代码中不清楚实际发生散列的位置,所以你需要检查user.password = new_password是否执行了所需的魔法,或者是否需要手动执行。它应该类似于最初用密码创建用户的代码。

的您是否获得了“哎呦密码不匹配'!”消息,如果你忘了行else从句前它的实际问题:

  if user is not None and user.validate_password(password): #encrypted way of checking password from DB 
       user.password = new_password 
      **else:** 
       message = 'Whoops! Passwords do not match.' 

2.密码重置为用户谁不知道他们当前的密码

一个简单的方法是将一个新的字段,如password_reset_secret,添加到您的用户模型。当一个健忘的人的类型在他们的电子邮件,你会发现通过电子邮件用户,填充password_reset_secret随机未猜测的胡言乱语,并用链接向用户发送电子邮件到一个特殊的页面,说https://yoursite.com/password_reset/jhg876jhgd87676

一旦接收到电子邮件,用户点击链接并访问“秘密”页面 - 这一点你知道他们有权访问他们输入的电子邮件地址,因为URL是不可猜测的,并且从任何地方都没有链接。在该页面上是一个包含新密码字段的表单。当他们输入一个新密码时,您从URL中查询password_reset_secret的用户对象并更新其密码。完成。

要使密码重置URL在一段时间后过期,您可以在User模型中添加另一个字段字段(比如说“password_reset_last_valid”),并在创建密码重置哈希时将其设置为“now + 3 days”当用户尝试访问链接时检查字段。如果该字段的值是过去,那么你只是假装没有发现任何东西。

为防止用户多次使用链接,只需在用户成功更改密码后清除password_reset_secretpassword_reset_last_valid字段。

3. URI是否宁静?

不,他们不是,但你可能不应该在这个阶段:)

+0

创建用户时,密码在创建时自动散列。我在db中有一个'validate_password()'函数,然后验证。看到我上面添加的链接。那样有用吗?还是应该在'new_password'代码中添加手动代码?你对2.密码重置的解释非常棒!我仍然不确定'password_reset_secret'在用户模型中的样子,因为它需要向用户发送电子邮件(文本)。我想象的视图代码只会要求用户的电子邮件,如果有效的发送,ELIF错误消息。 PS,你的解释非常清楚和启发。 – thesayhey

+0

@thesayhey:“创建用户时,密码会在创建时自动散列” - 所以您需要确保在设置新密码时密码也被散列。我想,'validate_password()'通过散列一个新密码的副本并将其与旧存储的散列密码进行比较来工作,但它不会修改您分配给user.password的值,除非有一些魔法地点。在更新密码 – Sergey

+0

后,只需在数据库中检查用户的“密码”字段的内容,不仅可以验证我的链接中显示的密码,还可以创建时自动散列。应该在表中创建一个新的函数,就像创建方法一样?至于第二部分,我在db中创建了一个'retrieve_user_byemail(email)'方法,通过电子邮件查询用户并返回用户对象的所有用户信息。我认为这将是views.py中的电子邮件发送链接片段?如果是这样,我不确定你提到的自动发送和乱码部分。 – thesayhey