drf认证
- 参考网址
https://www.cnblogs.com/yangyi215/p/15041975.html
-
作用: 校验用户是否登录
-
书写步骤
- 写一个类,继承BaseAuthentication,重写类中的重写authenticate方法,认证的逻辑写在其中
- 如果认证通过,返回两个值,一个值最终给了Requet.user,另外一个值给了Requet.auth
- 如果认证失败,抛出异常,APIException或者AuthenticationFailed
ps:从源码中看出,如果有多个认证,要将返回有两个值的放到最后(认证流程见参考网址)
- 认证demo
### models.py
# 用户
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
# token
class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to='User')
......
## your_app.auth.py
# 用户登录之前,校验token是否正确(即请求到LoginView之前,先经过这个认证类校验,通过了请求才到LoginView)
class MyAuthentication(BaseAuthentication):
# 须重写
def authenticate(self, request):
"""
如果认证通过,返回两个值 request.user和request.auth;
如果认证失败,抛出AuthenticationFailed异常
"""
# token = request.query_params.get('token')
token = request.data.get('token')
if token:
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token # 正确就返回user和token
else:
raise AuthenticationFailed('认证失败')
else:
raise AuthenticationFailed('请求地址中需要携带token')
## views
# 如果请求能来到这边,说明自定义 MyAuthentication 校验无误
class LoginView(APIView):
authentication_classes = [MyAuthentication,] # 局部配置
# uthentication_classes = []
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user_obj = User.objects.filter(username=username, password=password).first()
if user_obj:
token_num = str(uuid.uuid4())
# 登录成功以后,更新token
UserToken.objects.update_or_create(defaults={'token': token_num}, user=user_obj)
return Response({'status': 100, 'msg': '登陆成功', 'token': token_num})
else:
return Response({'status': 101, 'msg': '用户名或密码错误'})
- 全局配置如下
......
# ---------DRF配置--------------------#
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["apps.tests.auth.MyAuthentication"]
}
......
class LoginView(APIView):
# authentication_classes = [MyAuthentication,] # 局部配置
authentication_classes = [] # 或者 authentication_classes 不写
权限
- 流程
- APIView ---> dispatch方法 ---> self.initial(request, *args, **kwargs) ---> self.check_permissions(request)
- 源码解析:重写 has_permission()即可,返回布尔
......
def check_permissions(self, request):
# 遍历权限对象列表得到一个个权限对象(权限器),进行权限认证
for permission in self.get_permissions():
# 权限类一定有一个has_permission权限方法,用来做权限认证的
# 参数:权限对象self、请求对象request、视图类对象
# 返回值:有权限返回True,无权限返回False
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
- demo演示
from django.db import models
# 用户
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
# 新增用户类型
user_type = models.IntegerField(default=3, choices=(
(1, '超级用户'),
(2, '普通用户'),
(3, '游客'),
))
# token
class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to='User',on_delete=models.CASCADE)
# 认证
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from apps.tests import models
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 不同的请求方法,响应相应的token
if request.method == 'POST':
token = request.data.get('token')
else:
token = request.query_params.get('token')
"""
如果认证通过,返回两个值 request.user和request.auth;
如果认证失败,抛出AuthenticationFailed异常
"""
if token:
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token
print(user_token.user,token)
else:
raise AuthenticationFailed('token认证失败')
else:
raise AuthenticationFailed('请求地址中需要携带token')
# 权限
from rest_framework.permissions import BasePermission
class SuperUserPermission(BasePermission):
def has_permission(self,request,view):
# 意味view若引用这个权限类,则必须是超级用户才能访问这个view
if request.user.user_type == 1:
return True
else:
return False
# 视图
......
# 用户登录
class LoginView(APIView):
# 如果是全局配置,最好就是不写
authentication_classes = []
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user_obj = User.objects.filter(username=username, password=password).first()
if user_obj:
token_num = str(uuid.uuid4())
UserToken.objects.update_or_create(defaults={'token': token_num}, user=user_obj)
return Response({'status': 100, 'msg': '登陆成功', 'token': token_num})
else:
return Response({'status': 101, 'msg': '用户名或密码错误'})
# 用户登录以后,校验权限
class AfterLoginView(APIView):
# 不能写成空list,空list会导致生成匿名用户,而匿名用户是没有 user_type属性,会报错
# authentication_classes = []
permission_classes = [SuperUserPermission]
def get(self, request):
return Response({'msg':'欢迎超级用户...'})
- 测试接口: 如果权限不够,会提示权限不足,如何自定义权限不足的消息呢,看了源码,可以这么干
# 用户登录以后,校验权限
class AfterLoginView(APIView):
......
def get(self, request):
......
def permission_denied(self, request, message=None, code=None):
"""
If request is not permitted, determine what kind of exception to raise.
"""
message = '充值可以提升权限噢'
super().permission_denied(request, message=message, code=None)
## 或者这么写,显然很麻烦
# message = '充值可以提升权限噢'
# if request.authenticators and not request.successful_authenticator:
# raise exceptions.NotAuthenticated()
# raise exceptions.PermissionDenied(detail=message, code=code)
- 但是这么写,每一个视图难道都实现一次 permission_denied ? 显然不可接受,可以用继承类的方式来实现
??? 能不能写一个权限类,一次就搞定呢?
- 全局配置,一样的套路,注释掉原有的 permission_classes,settings配置即可
# ---------DRF配置--------------------#
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["apps.tests.auth.MyAuthentication"],
# 权限配置
"DEFAULT_PERMISSION_CLASSES": ["apps.tests.permissions.SuperUserPermission"]
}