【问题标题】:Apply Autocomplete=off to Django password reset confirm form将 Autocomplete=off 应用于 Django 密码重置确认表单
【发布时间】:2019-07-22 19:49:58
【问题描述】:

我将 Django 2.2 与 Python 3.7 一起使用

在进行安全审核后,我被要求确保应用程序中表单上所有与安全相关的字段都使用 autocomplete="off" 属性呈现。这是否是一个有效和有用的安全措施超出了范围......不幸的是。到目前为止,我已经通过扩展相关表格来实现这一点。例如,使用 PasswordResetForm(用户输入与帐户关联的电子邮件):

class NoAutocompletePasswordResetForm(PasswordResetForm):
    def __init__(self, *args, **kwargs):
        super(PasswordResetForm, self).__init__(*args, **kwargs)
        self.fields['email'].widget.attrs.update({'autocomplete': 'off'})

然后指示相关的身份验证视图像这样使用它(在 urls.py 中):

    url(r'^accounts/password_reset/?$', auth_views.PasswordResetView.as_view(form_class=forms.NoAutocompletePasswordResetForm)),

在使用 SetPasswordForm 的密码重置确认视图之前,这一直很好。第一次尝试是这样的:

url(r'^accounts/reset/(?P<uidb64>[0-9A-Za-z]+)/(?P<token>[0-9A-Za-z\-]+)/$', auth_views.PasswordResetConfirmView.as_view(form_class=forms.NoAutocompleteSetPasswordForm)),

class NoAutocompleteSetPasswordForm(SetPasswordForm):
    def __init__(self, *args, **kwargs):
        super(SetPasswordForm, self).__init__(*args, **kwargs)
        self.fields['new_password1'].widget.attrs.update({'autocomplete': 'off'})
        self.fields['new_password2'].widget.attrs.update({'autocomplete': 'off'})

然而,这只会在 super() 行上产生一个 TypeError,其详细信息为 __init__() got an unexpected keyword argument 'user',尽管该表单的 init 中存在名为 user 的参数,如 in the Django source 所示。但是为了有趣,我尝试将用户作为位置参数传递,如下所示:

class NoAutocompleteSetPasswordForm(SetPasswordForm):
    def __init__(self, *args, **kwargs):
        myKwargs = kwargs.copy()
        myUser = myKwargs['user']
        del myKwargs['user']

        myArgs = (*args, myUser)

        super(SetPasswordForm, self).__init__(*myArgs, **myKwargs)
        self.fields['new_password1'].widget.attrs.update({'autocomplete': 'off'})
        self.fields['new_password2'].widget.attrs.update({'autocomplete': 'off'})

通过这个调整,表单可以通过构建,但是 Django 在模板渲染期间出错。特别是,我得到以下堆栈跟踪:

Internal Server Error: /accounts/reset/MjA/set-password/
Traceback (most recent call last):
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 829, in _resolve_lookup
    current = current[bit]
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\forms\boundfield.py", line 66, in __getitem__
    raise TypeError
TypeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\core\handlers\base.py", line 145, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\core\handlers\base.py", line 143, in _get_response
    response = response.render()
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\response.py", line 106, in render
    self.content = self.rendered_content
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\response.py", line 83, in rendered_content
    content = template.render(context, self._request)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 171, in render
    return self._render(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 163, in _render
    return self.nodelist.render(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 937, in render
    bit = node.render_annotated(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 904, in render_annotated
    return self.render(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\defaulttags.py", line 309, in render
    return nodelist.render(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 937, in render
    bit = node.render_annotated(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 904, in render_annotated
    return self.render(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 987, in render
    output = self.filter_expression.resolve(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 671, in resolve
    obj = self.var.resolve(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 796, in resolve
    value = self._resolve_lookup(context)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\template\base.py", line 837, in _resolve_lookup
    current = getattr(current, bit)
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\forms\boundfield.py", line 74, in errors
    return self.form.errors.get(self.name, self.form.error_class())
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\forms\forms.py", line 180, in errors
    self.full_clean()
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\forms\forms.py", line 381, in full_clean
    self._clean_fields()
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\forms\forms.py", line 393, in _clean_fields
    value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
  File "C:\Users\Ben\Documents\Delorean\delorean_env_37\lib\site-packages\django\forms\widgets.py", line 258, in value_from_datadict
    return data.get(name)
AttributeError: 'User' object has no attribute 'get'

那么,如何让这个表单在 autocomplete='off' 的情况下呈现?

【问题讨论】:

  • myKwargs = dict(**kwargs) kwargs 已经是字典了。
  • 我这样做更多是为了获得一份副本而不是演员,但很好。我已将其更改为 kwargs.copy()。结果没有变化

标签: python django python-3.x django-forms


【解决方案1】:

在拉了很多头发之后,我开始工作了。从here 中汲取灵感,并基于我曾一度收到有关意外用户参数的错误这一事实,我将表单更改为以下内容:

class NoAutocompleteSetPasswordForm(SetPasswordForm):
    def __init__(self, user, *args, **kwargs):
        self.user = user

        super(SetPasswordForm, self).__init__(*args, **kwargs)
        self.fields['new_password1'].widget.attrs.update({'autocomplete': 'off'})
        self.fields['new_password2'].widget.attrs.update({'autocomplete': 'off'})

它现在根据需要使用自动完成属性呈现页面。

【讨论】:

    猜你喜欢
    • 2020-06-15
    • 2021-04-21
    • 1970-01-01
    • 2014-05-03
    • 2019-12-01
    • 2019-01-08
    • 2012-09-06
    • 2012-07-18
    • 1970-01-01
    相关资源
    最近更新 更多