二、REST framework的三大组件之一:认证

前置条件:新建视图类;并配置路由

class LoginView(MyAPIView):

    def get(self, request, *args, **kwargs):
        return Response('GET')


class UserView(MyAPIView):

    def get(self, request, *args, **kwargs):
        return Response('GET')


class OrderView(MyAPIView):

    def get(self, request, *args, **kwargs):
        return Response('GET')


url配置:
    path('api/<str:version>/LoginView/', views.LoginView.as_view()),
    path('api/<str:version>/UserView/', views.UserView.as_view()),
    path('api/<str:version>/OrderView/', views.OrderView.as_view()),

1、导入rest_framework的认证类,并导入认证失败时,需要触发的认证异常类

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

2、认证类基本用法

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed


# 新建认证类
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 获取请求中的token信息
        token = request.query_params.get('token')
        if token:
            # 认证成功,以元组的形式返回认证信息
            return "pass", token
        else:
            # 认证失败,返回认证失败信息
            raise AuthenticationFailed({'code': 100001, 'msg': '认证失败'})

    # 此方法可以认为注释方法,可有可无
    def authenticate_header(self, request):
        return 'API'


class LoginView(MyAPIView):

    # authentication_classes 为认证类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用认证类时,列表为空
    authentication_classes = []

    def get(self, request, *args, **kwargs):
        return Response('GET')


class UserView(MyAPIView):

    # 使用认证类时,列表中,添加认证类
    authentication_classes = [MyAuthentication, ]

    def get(self, request, *args, **kwargs):
        return Response('GET')


class OrderView(MyAPIView):

    # 使用认证类时,列表中,添加认证类
    authentication_classes = [MyAuthentication, ]

    def get(self, request, *args, **kwargs):
        return Response('GET')

# 接口访问结果:
# 1、LoginView正常返回
# 2、UserView不带token时,返回认证失败
# {
#     "code": "100001",
#     "msg": "认证失败"
# }
# 2.1 带token时,正常返回,OrderView同理

 3、 全局配置

3.1、新建python文件夹和 myAuthentication.py文件,并把认证类迁移到myAuthentication中

 3.2、通过源码可以知道,默认认证类调用的是APIView中的 DEFAULT_AUTHENTICATION_CLASSES 配置,所以settings添加全局配置

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None, # 用户配置
    # 认证配置 
    "DEFAULT_AUTHENTICATION_CLASSES":     
          ["auto_project.auth.myAuthentication.QueryParamsAuthentication",]
}

3.3、修改视图类,并再次访问接口

class LoginView(MyAPIView):

    # authentication_classes 为认证类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用认证类时,列表为空
    # 请注意,当类中使用authentication_classes=[认证类1, 认证类2, ...]时,认证默认使用就近原则,使用类中的authentication_classes
    authentication_classes = []

    def get(self, request, *args, **kwargs):
        return Response('GET')


class UserView(MyAPIView):

    def get(self, request, *args, **kwargs):
        return Response('GET')


class OrderView(MyAPIView):

    def get(self, request, *args, **kwargs):
        return Response('GET')

# 接口访问结果:
# 1、LoginView正常返回
# 2、UserView不带token时,返回认证失败
# {
#     "code": "100001",
#     "msg": "认证失败"
# }
# 2.1 带token时,正常返回,OrderView同理

 4、多个认证类应用

4,1、多个认证源码解析<只看运行过程,源码位于request中的initialize_request中<关注这个地方,学习权限时,源码位置有所不同>

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        # 认证类源码
        for authenticator in self.authenticators:
            try:
                # 循环认证类
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
            # 当认证类返回不为空时,赋值
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

4.2、通过源码知道当有多个认证类时,循环认证类,单个认证类返回为空时,再走下一个认证类,根据实际应用,认证条件可以放到请求体、头部或url中,基于此条件修改认证类

class QueryParamsAuthentication(BaseAuthentication):

    # 通过url获取token
    def authenticate(self, request):
        token = request.query_params.get('token')
        # 结合源码为空时,走下一个认证类
        if not token:
            return
        # 当token不为空时,从数据库中获取token
        db_data = UserInfo.objects.filter(token=token).first()
        # 当token数据库中token不为空时,返回数据数据对象和token
        if db_data:
            return db_data, token
        # 数据库无token时,返回空
        return

    def authenticate_header(self, request):
        return 'API'


# 通过头部获取
class HeaderAuthentication(BaseAuthentication):

    def authenticate(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')  # postmen发送请求时, 使用AUTHORIZATION, HTTP_ 为django自己拼接
        if not token:
            return
        db_data = UserInfo.objects.filter(token=token).first()
        if db_data:
            return db_data, token
        return

    def authenticate_header(self, request):
        return 'API'


# 通过Body体获取token
class BodyAuthentication(BaseAuthentication):

    def authenticate(self, request):
        token = request.data.get('token')
        if not token:
            return
        db_data = UserInfo.objects.filter(token=token).first()
        if db_data:
            return db_data, token
        return

    def authenticate_header(self, request):
        return 'API'


# 当认证类循环完成后,没有找到token时,才触发认证失败
class NoAuthentication(BaseAuthentication):

    def authenticate(self, request):
        raise AuthenticationFailed({'code': 100001, 'msg': '认证失败'})

    def authenticate_header(self, request):
        return 'API'

4.3、案例-用户登录和认证

1、models中添加表userInfo,并同步到数据库中

class UserInfo(models.Model):
    db_table = 'userinfo'
    username = models.CharField(verbose_name='用户名', max_length=32)
    password = models.CharField(verbose_name='密码', max_length=64)
    token = models.CharField(verbose_name='token', max_length=64)
    role = models.IntegerField(verbose_name='角色', choices=((1, '总监'), (2, '经理'), (3, '员工')), default=3)

2、数据库添加数据

 3、添加视图类,并配置url

class LoginAPIView(MyAPIView):

    # 不使用认证类时,列表为空
    authentication_classes = []

    def get(self, request):
        username = request.query_params.get('username')
        password = request.query_params.get('password')
        # 查询是否有账号密码
        db_userinfo = UserInfo.objects.filter(username=username, password=password).first()
        if not db_userinfo:
            return Response({'code': 100002, 'msg': '用户名密码错误'})
        # 如果账号密码和token都存在,返回
        elif db_userinfo and db_userinfo.token:
            return Response({'code': 200, 'token': db_userinfo.token, 'msg': 'suss'})
        # 如果token不存在,生成uuid
        token = str(uuid.uuid4())
        # 添加uuid到数据库中
        db_userinfo.token = token
        db_userinfo.save()
        # 返回添加完成的token
        return Response({'code': 200, 'token': token, 'msg': 'suss'})

4、访问接口生成token

 5、获取token信息后,再次访问之前的UserView接口,不加token时,因为有了全局认证配置,返回认证失败

6、在Params中加上token,再次访问,访问成功

7、把token放到头部信息中,再次访问,接口访问成功

8、body体中应用一致,不在演示, 关于认证返回的信息,在权限中可以用到,详细请学习权限模块功能点,连接:

https://i.cnblogs.com/posts/edit;postId=18632015

 

posted @ 2024-12-26 09:56  蜗牛·哥  阅读(37)  评论(0)    收藏  举报