DRF - 认证

认证组件


在DRF中,提交请求的时候不需要携带csrf,因为有token认证了

认证:通过判断token,来判断用户是否登录

1.登录接口编写

  • 需要和数据库交互,但是不需要序列化,所以使用ViewSetMixin + APIView

使用action装饰器装饰

  • 发送post请求,取出前端传入的数据-->request.data中获取

  • 判断是否成功 将session存在UserToken中存储登录状态

    如果存在则更新,不存在就更新

  • 返回给前端

(1)models.py - 模型层

建立User和UserToken表

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

class UserToken(models.Model):  
    # 跟User是一对一,存放token信息
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
    # user :反向,表名小写,所有有user字段

(2)urls.py - 路由

通过路由自动生成接口 - /api/v1/user/login/

from rest_framework.routers import SimpleRouter

# 生成路由
router = SimpleRouter()
router.register('user', views.UserView, 'user')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(router.urls)),
]

(3)views.py - 视图层

登录接口,自动生成路由 + ViewSet

  • 由于登录功能,不用序列化数据,所以可以继承ViewSet
class UserView(ViewSet):
    # 路由接口装饰器
    @action(methods=['POST'], detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        # 1.判断用户名是否存在
        if user:
            # 用户名存在
            # 1.1 生成一个随机字符串:通过uuid4生成
            token = str(uuid.uuid4())
            # 1.2 生成之后将其存在user_token中,用于登录的时候校验
            #     如果之前没有登录过,则没有token则新建一个。
            #     如果有token则更新token ==> 使用 update_or_create(传对象,defaults={'token':token)方法
            UserToken.objects.update_or_create(user=user,defaults={'token':token})
            return Response({'code': 100, 'msg': '用户登录成功'})
        else:
            return Response({'code': 101, 'msg': '用户名或者密码错误'})

2.认证功能

自定认证的使用步骤

(1)编写一个认证类,继承【认证模块】中的BaseAuthentication

from rest_framework.authentication import BaseAuthentication

(2)重写authenticate方法,来进行登录认证

  • 判断用户是否提供了token
  • 如果提供了token,则判断token是否一致

一般token都放在【请求头】中,而通过request.META来获取

(3)如果认证成功,返回【当前登录用户,token】

(4)如果认证不通过则通过AuthenticationFailed抛出异常

from rest_framework.exceptions import AuthenticationFailed

(5)全局认证与局部认证、局部禁用

3.全局认证与局部认证、局部禁用

全局、局部、默认配置的优先级

1.局部(视图类中)

2.全局(settings.py中)

3.默认(默认配置文件中)

认证类可以设置是全局使用或者是局部使用。

并且认证类可以配置多个,按照从前向后到顺序执行,如果前面有返回值,认证就不继续往下执行了

(1)局部认证:对于对应的视图类生效

在视图类中,通过authentication_classes来进行限制认证

class BookDetailView(ViewSetMixin, RetrieveAPIView):
    """查询单个"""
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    "局部认证"
    authentication_classes = [LoginAuth]

(2)全局认证:对于全局的视图类生效

settings.py在配置文件中配置

REST_FRAMEWORK = {
    # 【认证】的全局配置
    'DEFAULT_AUTHENTICATION_CLASSES':'app01.authenticate.LoginAuth',
}

(3)全局认证 + 局部禁用

settings.py 在配置文件中配置

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["mine.x_authenticate.LoginAuth", ]
}

views.py的视图类中

当全局都配置了需要认证后,只要在局部的视图类中authentication_classes空即可完成局部禁用的功能

class BookView(APIView):
    authentication_classes = []
    ...

不要在配置中乱导东西??

例如:在配置文件中导入from app01.authenticate import LoginAuth

则会报错,这是因为app01还没在程序中加载成功

django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.

4.代码演示

authenticate.py - 自己编写认证类

# 导入认证类
from rest_framework.authentication import BaseAuthentication
# 导入认证失败时抛出的异常字段
from rest_framework.exceptions import AuthenticationFailed
# 导入UserToken表
from .models import UserToken


class LoginAuth(BaseAuthentication):

    def authenticate(self, request):
        """
        阅读源码我们可以知道,需要重写 authenticate 方法,来进行登录认证。
        代码逻辑:如果请求中携带了正确的token则可以登录成功【token放在地址栏中】
        """
        # 1.获取token
        token = request.query_params.get('token', None)
        # 2.判断是否存在token
        if token:
            # 3.获取UserToken表中的token
            user_token = UserToken.objects.filter(token=token).first()
            # 4.判断UserToken表中是否记录了token
            if user_token:
                # 5.如果存在token则表示用户已经登录,则需要【固定】返回两个数据:(1)当前登录用户 (2)token
                return user_token.user, token
            else:
                # 6.如果没有登录则抛出异常
                raise AuthenticationFailed('用户未登录,token认证失败')
        else:
            # 7.前端没有传token抛出异常
            raise AuthenticationFailed('token未携带')

views.py - 视图类

from .serializer import BookSerializer
# 导入认证类
from .authenticate import LoginAuth

"2 查询所有 和 查询单个 的接口"
# 导入查询所有和查询单个的视图子类
from rest_framework.generics import ListAPIView, RetrieveAPIView
# 自动生成路由的魔法类
from rest_framework.viewsets import ViewSetMixin


class BookView(ViewSetMixin, ListAPIView):
    """查询所有"""
    queryset = Book.objects.all()
    serializer_class = BookSerializer


class BookDetailView(ViewSetMixin, RetrieveAPIView):
    """查询单个"""
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    "局部登录认证"
    authentication_classes = [LoginAuth]

urls.py - 路由

路由通过自动生成

# 生成路由
router = SimpleRouter()
router.register('user', views.UserView, 'user')

urlpatterns = [
    path('api/v1/', include(router.urls)),
]
posted @ 2023-02-07 19:33  Duosg  阅读(64)  评论(0编辑  收藏  举报