【问题标题】:RESTful update passwordRESTful 更新密码
【发布时间】:2016-03-13 22:40:30
【问题描述】:

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

我对使用最佳实践的工作流应该用于什么有点困惑:
1.) 为知道现有密码的用户更新密码
2.) 如果用户忘记了,则重置密码。
3.) URI(资源)是 RESTful 的吗?就这个网络应用而言,我只需要 GET 和 POST 来进行更改。

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

user = DBSession.query(User).filter_by(email=email).first() 如果用户: user.password = new_password

感谢您提供任何见解/意见。我是新手,想正确编码。

所有这些都是通过 HTML 而不是 JSON。
软件:Python 2.7.9、Pyramid 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

【问题讨论】:

  • 你应该使用一些身份验证(oauth 可以进行身份​​验证并且它仍然很安静)......你也不应该实际更改密码,而是通过电子邮件发送关联的电子邮件帐户链接到“更改密码”类型的东西
  • @JoranBeasley 我正在使用 Pyramid 的验证码登录。至于重置密码,我不知道该怎么做。当我使用 SQLAlchemy 和 Pyramid 时,你能指出一些不是 Django 的东西吗?也许示例伪代码?作为蓝图,这将非常有帮助。还是链接?谢谢!!!

标签: python rest sqlalchemy pyramid


【解决方案1】:

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 从 DB 中查询 User 对象并更新其密码。完成。

要使密码重置 URL 在一定时间后过期,您可以在 User 模型中添加另一个字段字段 - 例如,“password_reset_last_valid”,在创建密码重置哈希时将其设置为“现在 + 3 天”并在用户尝试访问链接时检查该字段。如果该字段的值是过去的值,那么您就假装什么也没找到。

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

3. URI 是否安静?

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

【讨论】:

  • 创建用户时,密码会在创建时自动散列。我在 db 中有一个 validate_password() 函数,然后验证。请参阅我在上面添加的链接。那样有用吗?还是应该在new_password 代码中添加手动代码?您对 2. 密码重置的解释太棒了!我仍然不确定 password_reset_secret 在 User 模型中的样子,因为它需要向用户发送电子邮件(文本)。我想视图代码会简单地询问用户电子邮件,如果有效发送,elif 错误消息。 PS,你的解释很清晰,很有启发性。
  • @thesayhey:“创建用户时,密码会在创建时自动进行哈希处理”——因此您需要确保在设置新密码时也对密码进行哈希处理。我想validate_password() 的工作原理是散列新密码的副本并将其与旧存储的散列密码进行比较,但它不会修改您分配给 user.password 的值,除非发生了一些魔术。更新密码后只需在数据库中检查用户password字段的内容
  • 不仅是我的链接中显示的验证密码和创建时的自动散列。表中是否应该存在一个像创建方法一样散列的新函数?至于第二部分,我在数据库中创建了一个retrieve_user_byemail(email) 方法,该方法通过电子邮件查询用户并返回包含所有用户信息的用户对象。我认为这就是views.py中的电子邮件发送链接片段?如果是这样,我不确定您提到的自动发送和乱码部分。
  • 是的,每次为password 字段赋值时都需要对密码进行哈希处理,例如user.password = hashlib.sha224(new_password).hexdigest()(尽管我担心这不是最安全的哈希方法密码,我们现在不要让事情复杂化)
  • 第二部分:使用Python的uuid模块生成唯一ID(即str(uuid.uuid4()):docs.python.org/2/library/uuid.html,然后使用pyramid_mailer发送邮件:docs.pylonsproject.org/projects/pyramid-mailer/en/latest
猜你喜欢
  • 2018-03-09
  • 2014-12-23
  • 1970-01-01
  • 1970-01-01
  • 2021-02-09
  • 2019-12-19
  • 2020-10-25
  • 2012-10-11
  • 1970-01-01
相关资源
最近更新 更多