如何不基于用户模型进行自定义身份验证
How to make custom authentication not based on User model
我的身份验证方案需要一些灵活性。通过这种灵活性,我的意思是我不想完全依赖用户模型或任何模型。在伪代码中我想得到这种逻辑:
class MyCustomAuthentication(authentication.BaseAuthentication)
def authenticate(self, request):
email = request.META.get('X_EMAIL')
password = request.META.get('X_PASSWORD')
# Here I want to connect to my database
# then make a query and verify if there exists a row that
# corresponds to email and password
# If it exists, then authentication is passed
# if not, then it is not passed
@api_view()
@authentication_classes((MyCustomAuthentication))
def items(request):
return Response({"message":"Hello world!"})
所以,如您所见,我不想依赖 ORM
,我只想用我的友善 sql
自己做所有事情。但我不知道我应该如何以及从身份验证中得到什么return。
您可以为不同形式的身份验证编写任意数量的身份验证后端。除非引发 PermissionDenied
异常,否则 Django 将尝试所有这些,并将 return 第一个匹配后端的结果。
但是,您的后端必须 return 类似用户的对象,或者 None
如果身份验证不成功。它不一定必须是模型,但它必须实现足够多的用户模型方法和属性才能用作模型。查看 Django 的 AnonymousUser
以获取非模型示例。
我 post 我在我的项目中使用了一些代码。
在settings.py
AUTHENTICATION_BACKENDS = (
'ai60.weixin.auth_backends.WeiXinAuthBackend',
'ai60.accounts.auth_backends.AI60AccountBackend',
'mezzanine.core.auth_backends.MezzanineBackend',
'django.contrib.auth.backends.ModelBackend',
)
在accounts/auth_backends.py
from __future__ import unicode_literals
from django.contrib.auth.backends import ModelBackend
from ai60.accounts.models import Account
from ai60.accounts.phone_token import PhoneTokenGenerator
from mezzanine.utils.models import get_user_model
User = get_user_model()
class AI60AccountBackend(ModelBackend):
"""
Extends Django's ``ModelBackend`` to allow login via phone and token, or
phone and password, or email and password.
"""
def authenticate(self, **kwargs):
if not kwargs:
return
if 'phone' in kwargs and 'token' in kwargs:
phone = kwargs.pop('phone', None)
request = kwargs.pop('request', None)
token = kwargs.pop('token', None)
phone_token = PhoneTokenGenerator(request)
if phone_token.check_token(phone, token) == '':
try:
user = Account.objects.get(phone=phone).user
return user
except Account.DoesNotExist:
return
if 'phone' in kwargs and 'password' in kwargs:
phone = kwargs.pop('phone', None)
password = kwargs.pop('password', None)
try:
user = Account.objects.get(phone=phone).user
if user.check_password(password):
return user
except Account.DoesNotExist:
return
if 'email' in kwargs and 'password' in kwargs:
email = kwargs.pop('email', None)
password = kwargs.pop('password', None)
try:
user = User.objects.get(email=email)
if user.check_password(password):
return user
except User.DoesNotExist:
return
然后,在我的登录表单中
class LoginForm(Html5Mixin, forms.Form):
"""
username: phone or email
"""
username = forms.CharField(label='phone or email')
password = forms.CharField(label='password',
widget=forms.PasswordInput(render_value=False))
def clean(self):
"""
Authenticate the given phone/email and password. If the fields
are valid, store the authenticated user for returning via save().
"""
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = None
if validate_phone(username):
user = authenticate(phone=username, password=password)
elif validate_email(username):
user = authenticate(email=username, password=password)
if user:
self._user = user
return self.cleaned_data
else:
raise forms.ValidationError('please enter valid account and password')
def save(self):
"""
Just return the authenticated user - used for logging in.
"""
return getattr(self, '_user', None)
Jacobian,需要引入@authentication_classes(...)装饰器。为此,只需在文件顶部添加以下行:
from rest_framework.decorators import authentication_classes
我的身份验证方案需要一些灵活性。通过这种灵活性,我的意思是我不想完全依赖用户模型或任何模型。在伪代码中我想得到这种逻辑:
class MyCustomAuthentication(authentication.BaseAuthentication)
def authenticate(self, request):
email = request.META.get('X_EMAIL')
password = request.META.get('X_PASSWORD')
# Here I want to connect to my database
# then make a query and verify if there exists a row that
# corresponds to email and password
# If it exists, then authentication is passed
# if not, then it is not passed
@api_view()
@authentication_classes((MyCustomAuthentication))
def items(request):
return Response({"message":"Hello world!"})
所以,如您所见,我不想依赖 ORM
,我只想用我的友善 sql
自己做所有事情。但我不知道我应该如何以及从身份验证中得到什么return。
您可以为不同形式的身份验证编写任意数量的身份验证后端。除非引发 PermissionDenied
异常,否则 Django 将尝试所有这些,并将 return 第一个匹配后端的结果。
但是,您的后端必须 return 类似用户的对象,或者 None
如果身份验证不成功。它不一定必须是模型,但它必须实现足够多的用户模型方法和属性才能用作模型。查看 Django 的 AnonymousUser
以获取非模型示例。
我 post 我在我的项目中使用了一些代码。
在settings.py
AUTHENTICATION_BACKENDS = (
'ai60.weixin.auth_backends.WeiXinAuthBackend',
'ai60.accounts.auth_backends.AI60AccountBackend',
'mezzanine.core.auth_backends.MezzanineBackend',
'django.contrib.auth.backends.ModelBackend',
)
在accounts/auth_backends.py
from __future__ import unicode_literals
from django.contrib.auth.backends import ModelBackend
from ai60.accounts.models import Account
from ai60.accounts.phone_token import PhoneTokenGenerator
from mezzanine.utils.models import get_user_model
User = get_user_model()
class AI60AccountBackend(ModelBackend):
"""
Extends Django's ``ModelBackend`` to allow login via phone and token, or
phone and password, or email and password.
"""
def authenticate(self, **kwargs):
if not kwargs:
return
if 'phone' in kwargs and 'token' in kwargs:
phone = kwargs.pop('phone', None)
request = kwargs.pop('request', None)
token = kwargs.pop('token', None)
phone_token = PhoneTokenGenerator(request)
if phone_token.check_token(phone, token) == '':
try:
user = Account.objects.get(phone=phone).user
return user
except Account.DoesNotExist:
return
if 'phone' in kwargs and 'password' in kwargs:
phone = kwargs.pop('phone', None)
password = kwargs.pop('password', None)
try:
user = Account.objects.get(phone=phone).user
if user.check_password(password):
return user
except Account.DoesNotExist:
return
if 'email' in kwargs and 'password' in kwargs:
email = kwargs.pop('email', None)
password = kwargs.pop('password', None)
try:
user = User.objects.get(email=email)
if user.check_password(password):
return user
except User.DoesNotExist:
return
然后,在我的登录表单中
class LoginForm(Html5Mixin, forms.Form):
"""
username: phone or email
"""
username = forms.CharField(label='phone or email')
password = forms.CharField(label='password',
widget=forms.PasswordInput(render_value=False))
def clean(self):
"""
Authenticate the given phone/email and password. If the fields
are valid, store the authenticated user for returning via save().
"""
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = None
if validate_phone(username):
user = authenticate(phone=username, password=password)
elif validate_email(username):
user = authenticate(email=username, password=password)
if user:
self._user = user
return self.cleaned_data
else:
raise forms.ValidationError('please enter valid account and password')
def save(self):
"""
Just return the authenticated user - used for logging in.
"""
return getattr(self, '_user', None)
Jacobian,需要引入@authentication_classes(...)装饰器。为此,只需在文件顶部添加以下行:
from rest_framework.decorators import authentication_classes