【发布时间】:2021-06-26 23:05:56
【问题描述】:
我知道这个话题已被广泛讨论,但大多数示例都是关于标准 Django 模板中的两因素身份验证,而在我的情况下,我想将两因素身份验证添加到一个项目中,其中 Django 用作 API后端,而前端是原生 VueJS 应用程序。
对于所有相关的身份验证,我使用的是内置的 Django 会话身份验证,因为前端和后端都部署在同一台服务器上。
我的问题是:如何在 django 用作 API 的项目中添加两因素身份验证(使用 google 身份验证器或 yubikey)?
问题出在这里:最简单的方法是让用户从前端登录,一旦用户从/accounts/login 登录(内置 django 身份验证视图),提交用户必须输入代码的表单。这种方法的问题是,一旦用户登录,Django 将创建一个会话,因此request.user.is_authenticated 将返回True,即使用户尚未提交两因素代码,所以一切都取决于前端。我不喜欢这种方法,因为我担心有人可能会找到一种方法来避免提交两因素表单并在网站的其余部分导航(因为根据 Django,该会话将被验证)而无需两因素身份验证
我尝试了什么:我仍然需要为此编写大部分代码,因为我想先了解它的安全性。但这是我的方法:
第一种方法
- 用户提交登录表单
- 提交登录表单后,带有凭据的 POST 请求将发送到我的 Django 应用程序中名为
/authenticate的端点。该端点将使用 Django 内置的authenticate()方法,该方法将检查这些凭据是否属于用户,而无需创建会话。 - 如果凭据属于用户,它将返回
True给用户。此时用户将提交带有 2FA 代码的表单,如果代码正确,请求将发送到/accounts/login,它将再次检查密码和电子邮件,并实际登录用户并创建会话。
第二种方法 另一种更好的方法是覆盖 Django-Allauth 登录视图,以便我可以添加对令牌的检查,例如(警告:伪代码):
if provided_code == user_code:
login()
return HttpResponse({'Result': 'Logged in!'})
else:
return HttpResponse({'Result': 'incorrect code'})
provided_code 是用户提供的 2FA 代码,user_code 是我将从 get_user_2fa_code(user) 等另一个函数中检索到的正确代码。
我不是安全专家,但我没有想出更好的方法。会有多安全?有没有更好的方法将 2FA 身份验证添加到 Django 项目中,其中 django 作为后端 API?
class LoginView(
RedirectAuthenticatedUserMixin, AjaxCapableProcessFormViewMixin, FormView
):
form_class = LoginForm
template_name = "account/login." + app_settings.TEMPLATE_EXTENSION
success_url = None
redirect_field_name = "next"
@sensitive_post_parameters_m
def dispatch(self, request, *args, **kwargs):
return super(LoginView, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(LoginView, self).get_form_kwargs()
kwargs["request"] = self.request
return kwargs
def get_form_class(self):
return get_form_class(app_settings.FORMS, "login", self.form_class)
def form_valid(self, form):
success_url = self.get_success_url()
try:
return form.login(self.request, redirect_url=success_url)
except ImmediateHttpResponse as e:
return e.response
def get_success_url(self):
# Explicitly passed ?next= URL takes precedence
ret = (
get_next_redirect_url(self.request, self.redirect_field_name)
or self.success_url
)
return ret
def get_context_data(self, **kwargs):
ret = super(LoginView, self).get_context_data(**kwargs)
signup_url = passthrough_next_redirect_url(
self.request, reverse("account_signup"), self.redirect_field_name
)
redirect_field_value = get_request_param(self.request, self.redirect_field_name)
site = get_current_site(self.request)
ret.update(
{
"signup_url": signup_url,
"site": site,
"redirect_field_name": self.redirect_field_name,
"redirect_field_value": redirect_field_value,
}
)
return ret
【问题讨论】:
标签: python django django-rest-framework