[英]Separately validating username and password during Django authentication
在django中使用標准身份驗證模塊時,失敗的用戶身份驗證不明確。 也就是說,似乎無法區分以下兩種情況:
我想我想在這兩種情況下向用戶顯示相應的消息,而不是單個“用戶名或密碼無效......”。
任何人都有使用簡單方法的經驗。 問題的症結似乎是在最低級別 - 在django.contrib.auth.backends.ModelBackend類中。 此類的authenticate()方法(以用戶名和密碼作為參數)僅在身份驗證成功時返回User對象,如果身份驗證失敗則返回None。 鑒於此代碼處於最低級別(好,最低級別高於數據庫代碼),繞過它似乎很多代碼被拋棄了。
簡單地實現新的身份驗證后端並將其添加到AUTHENTICATION_BACKENDS設置是最好的方法嗎? 可以實現返回(User,Bool)元組的后端,其中如果用戶名不存在,則User對象僅為None,如果密碼正確,則Bool僅為True。 但是,這會破壞后端與django.contrib.auth.authenticate()方法的約定( 記錄為在成功驗證時返回User對象,否則為None)。
也許,這一切都是無所顧忌? 無論用戶名或密碼是否不正確,用戶可能無論如何都必須轉到“忘記密碼”頁面,所以這可能都是學術性的。 我只是忍不住感覺,不過......
編輯:
關於我選擇的答案的評論:我選擇的答案是實現此功能的方法。 下面還有另一個答案,討論了這樣做的潛在安全隱患,我也將其視為提名的答案。 但是,我提名的答案解釋了如何實現此功能。 基於安全性的答案討論了是否應該實現這個功能,這實際上是一個不同的問題。
你真的不想區分這兩種情況。 否則,您將為潛在的黑客提供關於用戶名是否有效的線索 - 這對獲取欺詐性登錄有重大幫助。
這不僅僅是后端的功能,只是身份驗證表單。 只需重寫表單即可顯示每個字段所需的錯誤。 編寫一個使用新表單的登錄視圖,並將其設置為默認登錄URL。 (實際上我剛剛在最近的Django提交中看到,您現在可以將自定義表單傳遞給登錄視圖,因此這更容易實現)。 這應該花費大約5分鍾的努力。 你需要的一切都在django.contrib.auth中。
這里澄清的是目前的形式:
class AuthenticationForm(forms.Form):
"""
Base class for authenticating users. Extend this to get a form that accepts
username/password logins.
"""
username = forms.CharField(label=_("Username"), max_length=30)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
def __init__(self, request=None, *args, **kwargs):
"""
If request is passed in, the form will validate that cookies are
enabled. Note that the request (a HttpRequest object) must have set a
cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
running this validation.
"""
self.request = request
self.user_cache = None
super(AuthenticationForm, self).__init__(*args, **kwargs)
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive."))
elif not self.user_cache.is_active:
raise forms.ValidationError(_("This account is inactive."))
# TODO: determine whether this should move to its own method.
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))
return self.cleaned_data
def get_user_id(self):
if self.user_cache:
return self.user_cache.id
return None
def get_user(self):
return self.user_cache
加:
def clean_username(self):
username = self.cleaned_data['username']
try:
User.objects.get(username=username)
except User.DoesNotExist:
raise forms.ValidationError("The username you have entered does not exist.")
return username
我們必須在使用外部會員訂閱服務的網站上處理此問題。 基本上你做
from django.contrib.auth.models import User
try:
user = User.objects.get(username=whatever)
# if you get here the username exists and you can do a normal authentication
except:
pass # no such username
在我們的例子中,如果用戶名不存在,那么我們必須檢查由外部站點的Perl腳本更新的HTPASSWD文件。 如果文件中存在名稱,那么我們將創建用戶,設置密碼,然后執行身份驗證。
這個答案並不是Django特有的,但這是我用來完成這個的偽代碼:
//Query if user exists who's username=<username> and password=<password>
//If true
//successful login!
//If false
//Query if user exists who's username=<username>
//If true
//This means the user typed in the wrong password
//If false
//This means the user typed in the wrong username
def clean_username(self):
"""
Verifies that the username is available.
"""
username = self.cleaned_data["username"]
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return username
else:
raise forms.ValidationError(u"""\
This username is already registered,
please choose another one.\
""")
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.