我正在尝试在Django中使用尽可能多的内置功能编写一个登录端点的单元测试。
已有测试确认帐户创建端点运行正常。
在登录视图中,check_password()
函数返回的是True
,而authenticate()
函数返回的是None
。
使用check_password()
函数是否安全?
否则,我如何更新这段代码来使用authenticate()
函数?
accounts.py
class Account(AbstractUser):
username = models.CharField(max_length=150, unique=True, null=False, default='')
password = models.CharField(max_length=100)
...
REQUIRED_FIELDS = ['username', 'password']
class Meta:
app_label = 'accounts'
db_table = 'accounts_account'
objects = models.Manager()
test_login.py
def test_login(self):
# Create account
request_create = self.factory.post('/accounts/account',
self.request_data,
content_type='application/json')
view = AccountView.as_view()
response_create = view(request_create)
# Login account
request_login = self.factory.post('/accounts/login',
self.request_data,
content_type='application/json')
view = LoginView.as_view()
response = view(request_login)
views.py
class LoginView(View):
def post(self, request):
r = json.loads(request.body)
username = r.get('username')
password = r.get('password')
cp = check_password(password, Account.objects.get(username=username).password)
user = authenticate(username=username, password=password)
注:我已经检查了这个线程,is_active
被设置为true。
安全。主要区别在于,通过使用check_password(),您将从身份验证后端手动检查User,因此您必须检索User对象并将其密码与纯文本进行比较,就像在:
所做的那样check_password(password, Account.objects.get(username=username).password)
然而,使用authenticate()可以根据多个身份验证后端检查凭据。这意味着使用前者,您不能将应用程序与其他身份验证源挂钩
您在tests.py
和views.py
中缺少一些代码。话虽如此,这里是这个LoginView
的完整测试:
tests.py
class TestClientLogin(TestCase):
def setUp(self):
User = get_user_model()
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='test_user',
email='test_user@example.com',
password='super_secret'
)
def test_user_login(self):
request_data = {'username': 'test_user', 'password': 'super_secret'}
request = self.factory.post(
'/accounts/login/',
request_data ,
content_type='application/json'
)
response = LoginView.as_view()(request)
data = json.loads(response.content)
self.assertEqual(self.user.username, data['username'])
self.assertEqual(response.status_code, 200)
views.py
class LoginView(View):
def post(self, request):
data = json.loads(request.body)
username = data.get('username')
password = data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
return JsonResponse({'username': user.username})
else:
return JsonResponse({'username': None})
如果你想使用尽可能多的内置功能:
<标题>settings.py:AUTH_USER_MODEL = 'accounts.Account'
<标题>账户/models.py:class Account(AbstractUser):
# you dont need to define username or password again
...
REQUIRED_FIELDS = ['username', 'password']
class Meta:
# you don't need to define default app_label, db_table
objects = UserManager() # otherwise don't work "python manage.py createsuperuser"
<标题>账户/views.py:from django.contrib.auth.views import LoginView
class ClientLogin(LoginView):
pass
<标题>账户/tests.py:from django.test import TestCase, Client
from django.utils.crypto import get_random_string
class TestClientLogin(TestCase):
def setUp(self):
User = get_user_model()
self.username, self.password = get_random_string(20), get_random_string(20) # don't use constants in tests
self.user = User.objects.create_user(username=self.username, password=self.password )
def test_user_login(self):
request_data = {'username': self.username, 'password': self.password}
response = Client().post('/accounts/login/', request_data)
self.assertEqual(response.status_code, 302)
<标题>但是:你不需要测试ClientLogin
,你没有任何代码在视图中