Django默认情况下将用户名视为区分大小写,现在用于身份验证我已经编写了自己的Authentication Backend
来处理身份验证时不区分大小写的用户名。
如下所示:http://blog.shopfiber.com/?p=220
现在,问题是:
我有各种各样的观点和实用方法,可以将username
与一些叮咬进行比较。
即。
request.user.username == username_from_some_other_system_as_str
现在,如果用户名是yugal
,那么:
request.user.username == 'Yugal' # Returns False
现在,它应该返回True
[我想要实现的目标]
为此我记得C++
天,Operator Overloading
。
但我不认为只是为django的auth user
做这件事是个好主意,因为auth user
与django
紧密相连。
此外,重载==
将使整个班级不区分大小写,而不仅仅是username
字段。
那么,即使在整个过程中进行比较,我应该如何解决这个username
不区分大小写的问题。
注意:
创建一个始终返回小写用户名的get_username
方法是不可能的,因为它需要重新考虑所有代码才能使用它。您可以为您的代码执行一次,但如果您使用的是第三方django应用程序则无法实现。
我知道user.username.lower() = something.lower()
是可能的,但容易出错,而不是针对多开发人员设置中经常使用的内容的写入解决方案。
我尽可能使用SomeModel.objects.filter(username__iexact=username)
。但这仍然让系统容易受到任何不知情的开发人员的错误。
======================================
从概念上找出解决方案,但无法使其发挥作用(帮助):
####### Custom CharField for username case-insensitivity #######
from django.db.models.fields import CharField
class iUnicode:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if isinstance(other, str) or isinstance(other, unicode):
return self.value.lower() == other.lower()
if isinstance(other, self.__class__):
return other == self.value
def __unicode__(self):
return unicode(self.value)
def __str__(self):
return self.__unicode__()
class UsernameCharField(CharField):
def to_python(self, value): # Its not getting called
unicode_val = super(CharField, self).to_python(value)
return iUnicode(unicode_val)
if User._meta.local_fields[1].name == 'username':
User._meta.local_fields[1] = UsernameCharField(max_length=30)
User._meta.local_fields[1].model = User
################################################################
我假设to_python
用于将从数据库接收的值转换为python中的unicode
。但是,我想我的to_python
没有被调用。
这也将确保第三方应用中的不区分大小写,并且不需要任何重新分解。它将修补
的User
的核心。我将把它添加到我的第一个__init__.py
INSTALLED_APP
我做错了什么?
答案 0 :(得分:26)
从Django 1.5开始,使用户名不区分大小写非常简单:
class MyUserManager(BaseUserManager):
def get_by_natural_key(self, username):
return self.get(username__iexact=username)
答案 1 :(得分:6)
我在注册和登录过程中修改了几行似乎对我有用。使用我的解决方案,用户名仍将显示为用户在注册时编写的用户名,但不允许其他人使用不同的用户名。它还允许用户登录而不必担心编写区分大小写的用户名。
我修改了注册表单以搜索不区分大小写的用户名。
这是我对用户名的验证,它使用此用户名搜索用户。
User._default_manager.get(username__iexact=username)
然后我需要允许用户使用不区分大小写的用户名登录。
从我的登录视图:
username = request.POST['username']
password = request.POST['password']
caseSensitiveUsername = username
try:
findUser = User._default_manager.get(username__iexact=username)
except User.DoesNotExist:
findUser = None
if findUser is not None:
caseSensitiveUsername = findUser.get_username
user = auth.authenticate(username=caseSensitiveUsername, password=password)
答案 2 :(得分:4)
终于明白了:
对User
模型进行了如此多的实验和最小的影响,最终实现了它。
[感谢先生。 @freakish 用于不同的想法]
这是:
############ username case-insensitivity ############
class iunicode(unicode):
def __init__(self, value):
super(iunicode, self).__init__(value)
self.value = value
def __eq__(self, other):
if isinstance(other, str) or isinstance(other, unicode):
return self.value.lower() == other.lower()
if isinstance(other, self.__class__):
return other == self.value
def custom_getattribute(self, name):
val = object.__getattribute__(self, name)
if name == "username":
val = iunicode(val)
return val
def auth_user_save(self, *args, **kwargs): # Ensures lowercase usernames
username = self.username
if username and type(username) in [unicode, str, iunicode]:
self.username = username.lower() # Only lower case allowed
super(User, self).save(*args, **kwargs)
User.__getattribute__ = custom_getattribute
User.save = MethodType(auth_user_save, None, User)
#####################################################
我测试了它,它按预期工作。 :d
所以,这里是 testcases :
from django.test.testcases import TestCase
def create_user(data='testuser'):
email = '%s@%s.com' % (data, data)
user = G(User, username=data, email=email, is_active=True)
user.set_password(data)
user.save()
return user
class UsernameCaseInsensitiveTests(TestCase):
def test_user_create(self):
testuser = 'testuser'
user = create_user(testuser)
# Lowercase
self.assertEqual(testuser, user.username)
# Uppercase
user.username = testuser.upper()
user.save()
self.assertEqual(testuser, user.username)
def test_username_eq(self):
testuser = 'testuser'
user = create_user(testuser)
self.assertTrue(isinstance(user.username, iunicode))
self.assertEqual(user.username, testuser)
self.assertEqual(user.username, testuser.upper())
self.assertTrue(user.username == testuser.upper())
self.assertTrue(testuser.upper() == user.username)
self.assertTrue(user.username == iunicode(testuser.upper()))
对数据库的隐式不区分大小写查询
###################### QuerySet #############################
def _filter_or_exclude(self, negate, *args, **kwargs):
if 'username' in kwargs:
kwargs['username__iexact'] = kwargs['username']
del kwargs['username']
if args or kwargs:
assert self.query.can_filter(),\
"Cannot filter a query once a slice has been taken."
from django.db.models import Q
clone = self._clone()
if negate:
clone.query.add_q(~Q(*args, **kwargs))
else:
clone.query.add_q(Q(*args, **kwargs))
return clone
from django.db.models.query import QuerySet
QuerySet._filter_or_exclude = _filter_or_exclude
#############################################################
这将允许User.objects.get(username='yugal')
& User.objects.get(username='YUGAl')
会产生相同的用户。
答案 3 :(得分:0)
这个猴子补丁看起来像个坏主意。你将来肯定会遇到一些问题(Django会在幕后做很多事情)。我强烈建议您重新设计自己的应用。
但是,您可以尝试使用iUnicode
类):
def new_getattribute( self, name ):
val = object.__getattribute__( self, name )
if name == "username":
val = iUnicode( val )
return val
User.__getattribute__ = new_getattr
现在,我不是百分之百,这将是有效的,而且有点hacky,所以谨慎使用它。 :)
答案 4 :(得分:0)
有一种相对干净的方法:
# Case-insensitive django authentication, modified from
# http://justcramer.com/2008/08/23/logging-in-with-email-addresses-in-django/
# See also https://github.com/dabapps/django-email-as-username
# And https://docs.djangoproject.com/en/dev/topics/auth/customizing/#auth-custom-user
from django.contrib.auth.models import User
class EmailOrUsernameModelBackend(object):
def authenticate(self, username=None, password=None):
username = username.lower() # Force all usernames & email to all lower case
if '@' in username:
kwargs = {'email': username}
else:
kwargs = {'username': username}
try:
user = User.objects.get(**kwargs)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
def my_password_reset(request, **kwargs):
# Override django.contrib.auth.views.password_reset not needed because django does
# SELECT FROM "auth_user" WHERE UPPER("auth_user"."email"::text) = UPPER(E'xxx@emaple.com')
# But note you may want to manually create an UPPER index in the database for speed.
return password_reset(request, **kwargs)
然后设置
AUTHENTICATION_BACKENDS = (
'obviously.backends.EmailOrUsernameModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
您还必须在注册工作流程中强制用户名小写
这一切都运行正常,但不保留用户给出的情况,也没有效率查询数据库。默认的django行为是设计使然,请参阅https://code.djangoproject.com/ticket/2273
答案 5 :(得分:0)
使用不区分大小写的用户名的最简单方法是从默认ModelBackend
继承并覆盖authenticate
方法。
请注意,在except
块内我们正在执行UserModel().set_password(password)
,通过这样做,我们减少了其他工作时间。 Fixed bug report
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from users.models import User
class CaseInsensitiveModelBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
d = {'%s__iexact'%UserModel.USERNAME_FIELD: username}
user = UserModel.objects.get(**d)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
return None
并将此后端添加到settings.py
中的AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = (
'sdcpy.backends.CaseInsensitiveModelBackend', # inherits from 'django.contrib.auth.backends.ModelBackend'
)
答案 6 :(得分:0)
使用Togglebox1.Caption := 'Line 1' + LineEnding + 'Line 2';
是获得不区分大小写的用户名而不弄乱其他东西的最简单方法之一。
示例代码(Models.py):
UserManager
你很好! Source