Django 自定义身份验证后端 - "is_valid"调用身份验证



我第一次做我的自定义认证后端,它通常工作,但我不能理解一件事或两件事,在我看来有一些不想要的行为。

基本上我只是想添加一个额外的必填字段-域名,这将必须填写(或选择)登录

所以我有我的自定义DomainUser (AbstractUser),它定义了额外的"域名"。字段

class DomainUser(AbstractUser):
domainname = models.CharField(max_length=50)
REQUIRED_FIELDS = ['domainname', ]
def __str__(self):
return self.username
@property
def domainusername(self):
return ''.join([self.domainname, '\', self.username])

然后是视图:

def domain_login(request):
if request.method == 'POST':
login_form = DomainLoginForm(data=request.POST)
if login_form.is_valid():
username = login_form.cleaned_data.get('username')
domainname = login_form.cleaned_data.get('domainname')
raw_password = login_form.cleaned_data.get('password')
user = authenticate(username=username, domainname=domainname, password=raw_password)
if user is not None:
login(request, user)
return HttpResponseRedirect(reverse('admin:index'))
else:
pass # should return invalid logon page
else:
login_form = DomainLoginForm()

context = {
'login_form': login_form,
}
return render(request, 'domain_auth/login.html', context)

和auth:

class DomainLDAPBackend(BaseBackend):
def authenticate(self, request, username=None, domainname=None, password=None):
#return DomainUser.objects.get(username__iexact=username, domainname__iexact=domainname)
return DomainUser.objects.get(username__iexact=username)
def get_user(self, user_id):
try:
return DomainUser.objects.get(pk=user_id)
except DomainUser.DoesNotExists:
return None

Auth现在是一个完整的占位符(它没有真正验证使用域,但这只是暂时的),它只是为了测试目的,在我连接这里我的真正的Auth模块。

我的loginform有域名字段,以及它的工作。

我有两个问题:我填写我的登录表单,视图被称为:

  1. 似乎"authenticate"在我的"domain_login"查看,第一次是由" is_validate "方法,当它第一次传递"域名"参数为None。为什么这里叫它?这是正常的行为吗?我在文档中找不到这样的东西,但我想我的代码中有错误。在我看来,第二个手动调用是正确的-所有参数都像预期的那样传递,登录也一样有效。

  2. get_user方法的目的是什么?它只在登录后调用(在使用admin时,特别是在浏览用户模型时),但在验证时不会调用。

谢谢。

试试这个:

认证后端

# Override ModelBackend instead of BaseBackend, so you get various methods out of the box
class DomainLDAPBackend(ModelBackend):
# Don' t set default values for username, domainname and password (so they are manadatory)
def authenticate(self, request, username, domainname, password):
try:
# iexact is used by default
user = DomainUser.objects.get(username=username, domainname=domainname)
# Checks the password
if user.check_password(password):
return user
except ObjectDoesNotExist:
return

如果您希望用户可以在登录更改authenticate时选择域名(我建议您在注册时请求域名):

def authenticate(self, request, username, domainname, password):
try:
user = DomainUser.objects.get(username=username)
if user.check_password(password):
if user.domainname is None:
user.domainname = domainname
user.save()
return user
except ObjectDoesNotExist:
return

def domain_login(request):
if request.method == 'POST':
login_form = DomainLoginForm(data=request.POST)
# There is not need to call login_form.is_valid: access POST data directly, the validation is performed by DomainLDAPBackend
username = request.POST.get('username')
domainname = request.POST.get('domainname')
raw_password = request.POST.get('password')
user = authenticate(username=username, domainname=domainname, password=raw_password)
if user is not None:
login(request, user)
return HttpResponseRedirect(reverse('admin:index'))
else:
pass # Should return invalid login page
else:
login_form = DomainLoginForm()

return render(request, 'domain_auth/login.html', {'login_form': login_form})

注意,不需要重写与您的自定义用户模型不冲突的ModelBackend.get_user。这个方法被Django内部用来检索登录用户的信息。请记住在设置中设置AUTHENTICATION_BACKENDSAUTH_USER_MODEL

编辑

我在GitHub上分析了AuthenticationForm的代码,我发现它已经在validate中验证了用户(这就是为什么你的原始代码两次调用authenticate的原因)。首先,您需要覆盖validate,默认情况下不使用自定义后端方法:

class DomainLoginForm(AuthenticationForm):
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
domainname = self.cleaned_data.get('domainname')
if username is not None and password is not None and domainname is not None:
self.user_cache = authenticate(self.request, username=username, domainname=domainname, password=password)
if self.user_cache is None:
raise self.get_invalid_login_error()
else:
self.confirm_login_allowed(self.user_cache) # Raises an exception if the user is not active, you can override this method
return self.cleaned_data

在你的视图中,你可以简单地写:

def domain_login(request):
if request.method == 'POST':
login_form = DomainLoginForm(data=request.POST)
if login_form.is_valid():
# If the form is valid, DomainLoginForm.get_user() never returns None
user = login_form.get_user()
login(request, user)
return HttpResponseRedirect(reverse('admin:index'))
else:
login_form = DomainLoginForm()

return render(request, 'domain_auth/login.html', {'login_form': login_form})

最新更新