8.用户认证与权限
大约 4 分钟学习笔记测试平台开发
一、 用户注册
注册流程:
1. 注册序列化器
# serializers.py
# 注册序列化器
class RegisterSerializer(serializers.ModelSerializer):
# admin_code 不在user 模型字段中,需要单独定义
admin_code = serializers.CharField(default='') # 字符串类型
class Meta:
model = User
fields = ['username', 'password', 'email', 'phone', 'realname', 'admin_code']
# 额外的验证 --- 覆盖父类的方法
def validate(self, attrs): # attrs 为入参的字典形式
# 检查 admin_code 是否等于 sqpt
if attrs['admin_code'] and attrs['admin_code'] != 'sqpt':
raise ValidationError('错误的admin_code')
return attrs # 检查通过,返回入参数据
# 定义注册用户方法
def register(self):
in_param = self.data # 从序列化器的数据取入参
# 传递 admin_code
if in_param['admin_code']:
# 创建 用户数据不需要 admin_code
in_param.pop('admin_code')
user = User.objects.create_superuser(**in_param) # 解包 传递入参用于创建用户
else:
in_param.pop('admin_code')
user = User.objects.create_user(**in_param)
return user
2. 注册视图
# views.py
# 注册视图
@api_view(['POST'])
def register(request):
# 序列化器,获取注册信息
serializer = RegisterSerializer(data=request.data)
if serializer.is_valid(): # 自动根据模型定义判断数据入参是否合法
# 创建用户
user = serializer.register()
# 实现用户登录
auth.login(request, user) # 将当前用户信息记录到请求中,并在服务器的 session中记录信息
return Response(status=status.HTTP_201_CREATED,data={'msg': 'reister success', 'retcode': 201,'is_admin': user.is_superuser})
return Response(status=400,data={'msg': 'error', 'error': serializer.errors,'retcode': 400}) # 如果数据校验不合法就返回错误信息
3. 注册 URL
path('register/', views.register), # 注册
二、 用户登录
登录流程:
1. 登录序列化器
# serializers.py
# 登录
class LoginSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'password',]
def validate(self, attrs):
# 判断用户名和密码是否传递
position_params = ['username', 'password'] # 必填参数
for param in position_params:
if param not in attrs: # 如果入参中不存在必填参数,抛出异常
raise ValidationError(f'缺少参数:{param}')
# 验证用户信息
user = auth.authenticate(**attrs)
if not user:
raise ValidationError('用户名或密码错误')
return user
2. 登录视图
# views.py
# 登录视图
@api_view(['POST'])
def login(request):
serializer = LoginSerializer(data=request.data)
user = serializer.validate(request.data)
if user:
# 验证成功
auth.login(request, user)
return Response(status=302, data={'msg': 'success', 'to': 'index.html'}) # 登录成功,跳转主页
return Response(status=status.400,data={'msg': 'error', 'error': serializer.errors, 'retcode': status.400})
3. 登录 url
path('login/', views.login), # 登录
4. 渲染器-异常捕获重写
# exception.py
# REST 框架异常处理器
from rest_framework.exceptions import ValidationError
from rest_framework.views import exception_handler
# 处理异常返回 过滤器
def my_exception_handler(exc, context):
# 获取标准的错误响应
response = exception_handler(exc, context)
if response:
# 成功捕获到异常
# 判断 exc 类型,如果是 APIException 类型
if isinstance(exc, ValidationError):
error_msg = exc.detail[0]
else:
error_msg = exc
response.data = {'msg': 'error', 'retcode': response.status_code, 'error': str(error_msg)}
return response
三、 用户登出
流程图:
1. 登出视图
# views.py
# 登出请求
@api_view(['GET'])
def logout(request):
if request.user.is_authenticated:
auth.logout(request) # 清除登录信息 --- session 中
return Response(status=302, data={'msg': 'logout success', 'retcode': 302, 'to': 'login.html'})
2. 登出 url
path('logout/', views.logout),
3. 获取当前用户信息视图
前后端分离的系统前端部分需要同步后端的访问状态,因此构造一个请求用于检查是否登录
# views.py
# 获取当前用户信息
@api_view(['GET'])
def current_user(request):
# 判断当前用户是否处于登录状态 request.user
if request.user.is_authenticated: # 验证用户是否登录
# 返回当前用户登录信息
serializer = UserSerializer(request.user)
return Response(serializer.data)
# 如果为登录就返回一个重定向地址
return Response(status=status.HTTP_403_FORBIDDEN,data={'retcode':403,'msg':'未登录', 'to': 'login.html'})
四、 用户状态效验
1. Django 默认方式
django 默认的用户信息存储方案为 session 机制,有内置的装饰器可以验证当前请求是否携带用户认证信息,
from django.contrib.auth.decorators import login_required
@login_required
def list_user(request):
...
# 如果用户未登录就会被重定向到一个默认的URL(/accounts/login/) 状态码为403
2. DRF 设置认证方案
可以使用 DEFAULT_AUTHENTICATION_CLASSES
设置全局的默认身份验证方案
# settings.py
REST_FRAMEWORK = {
# 全局认证模块
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
①. 基于 APIView 类视图的方式 :
# views.py
from rest_framework.authentication import SessionAuthentication,BasicAuthentication
from rest_framework.permissions import IsAuthenticated
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
# 权限相关
authentication_classes = (SessionAuthentication, BasicAuthentication)
# IsProjectAdmin 为自定义的权限
permission_classes = (IsAuthenticated, IsProjectAdmin)
②. 基于函数的视图
# views.py
from rest_framework.authentication import SessionAuthentication,BasicAuthentication
from rest_framework.permissions import IsAuthenticated
# 查询所有用户
@api_view(['GET'])
@authentication_classes((SessionAuthentication, BasicAuthentication)) # 认证类
@permission_classes((IsAuthenticated,))
def user_lest(request):
query_set = User.objects.all()
serializer = UserSerializer(query_set, many=True)
return Response(serializer.data)
3. DRF 权限方案
①. 定义权限
sqpt/permissions.py
# permissions.py
from rest_framework import permissions
class IsProjectAdmin(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# obj 是当前数据对象,此时等于当前项目数据对象
# 如果请求是 GET、HEAD、OPTIONS 等安全类型的请求
if request.method in permissions.SAFE_METHODS:
return True
# 返回当前用户是否是项目(obj)管理员,只有当前项目管理员才具备操作项目权限
return obj.admin == request.user
②. 加入权限
# views.py
from rest_framework.authentication import SessionAuthentication,BasicAuthentication
from rest_framework.permissions import IsAuthenticated
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
# 权限相关
authentication_classes = (SessionAuthentication, BasicAuthentication)
# IsProjectAdmin 为自定义的权限
permission_classes = (IsAuthenticated, IsProjectAdmin)