Django drf:认证及组件、token、局部钩子源码分析

本文目录:

一、drf认证功能

二、token讲解

三、局部钩子源码分析

一、drf认证功能

  1.认证简介:

    只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录则不能查看。这时候需要用到认证组件

  2.局部使用

    models层

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


class UserToken(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to='User')

    新建认证类(验证通过return两个参数)

from rest_framework.authentication import BaseAuthentication

class Authlogin(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        res = models.UserToken.objects.filter(token=token).first()
        if res:
            # 说明这个人登录了
            # return None
            return res.user, token
        else:
            # 说明没有登录
            raise NotAuthenticated("您没有登录")

    views层

def get_token(name):
    m = hashlib.md5(name.encode("utf-8"))
    print(m.hexdigest())
    res = m.hexdigest()
    return res


class Login(APIView):
    #authentication_classes = []
    def post(self, request):
        response = {'status': 100, 'msg': None}
        # 把用户名和密码取到
        name = request.data.get('name')
        pwd = request.data.get('password')
        print(name, pwd)
        # 取出数据库的用户数据
        user = models.User.objects.filter(name=name, password=pwd).first()
        print(user)
        if user:
            response['msg'] = '登录成功!'
            # 随机字符串可以是用户名+当前同时生成md5
            # uuid
            token = get_token(name)

            # user = user 查询条件 defaults={'token':token}
            models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
            response['token'] = token
        else:
            # response['status']后面是等于号不是冒号
            response['status'] = 101
            print(response['status'])
            response['msg'] = '用户名或密码错误'
        return Response(response)
def get_token(id,salt='123'):
    import hashlib
    md=hashlib.md5()
    md.update(bytes(str(id),encoding='utf-8'))
    md.update(bytes(salt,encoding='utf-8'))

    return md.hexdigest()+'|'+str(id)

def check_token(token,salt='123'):
    ll=token.split('|')
    import hashlib
    md=hashlib.md5()
    md.update(bytes(ll[-1],encoding='utf-8'))
    md.update(bytes(salt,encoding='utf-8'))
    if ll[0]==md.hexdigest():
        return True
    else:
        return False

class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        success=check_token(token)
        if success:
            return
        else:
            raise AuthenticationFailed('认证失败')
    def authenticate_header(self,request):
        pass
class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':1001,'msg':None}
        try:
            name=reuquest.data.get('name')
            pwd=reuquest.data.get('pwd')
            user=models.User.objects.filter(username=name,password=pwd).first()
            if user:
                token=get_token(user.pk)
                # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                back_msg['status']='1000'
                back_msg['msg']='登录成功'
                back_msg['token']=token
            else:
                back_msg['msg'] = '用户名或密码错误'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)
from rest_framework.authentication import BaseAuthentication
class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
            return
        else:
            raise AuthenticationFailed('认证失败')
    def authenticate_header(self,request):
        pass

class Course(APIView):
    authentication_classes = [TokenAuth, ]

    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')
不存数据库的token验证
-{name:lqz,id:1}
 -把{name:lqz,id:1} 用我自己知道的加密方式加密之后变成了:asdfasdf
 
 -asdfasdf|{name:lqz,id:1}  当做token,发到客户端
 -以后客户端再发请求,会携带asdfasdf|{name:lqz,id:1}过来
 -服务端截取{name:lqz,id:1},再用我的加密方式加密:asdfasdf
 -拿到加密后的串:asdfasdf和请求的asdfasdf比较,如果一样
 -假设它模拟成bsdfasdf|{name:lqz,id:1,time:2019-10-13}
 bsdfasdf|{name:lqz,id:1}
 -token好处是:服务端不需要存session了
 

总结:局部使用,只需要在试图类里加入

authentication_classes = [TokenAuth, ]

  3.全局使用

    *一般会将继承BaseAuthentication的token类抽出来单独放在一个auth.py文件中

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import NotAuthenticated
from app import models

class Authlogin(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        res = models.UserToken.objects.filter(token=token).first()
        if res:
            # 说明这个人登录了
            # return None
            return res.user, token
        else:
            # 说明没有登录
            raise NotAuthenticated("您没有登录")

    *在setting文件中配置全局认证属性

# ["app.auth.Authlogin", ]中的Authlogin要与auth.py文件中视图类大小写一致
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app.auth.Authlogin", ]
}

    * 需要在login中加入局部禁用

    authentication_classes = []

二、token讲解

   -认证功能:
      1 写一个类,继承BaseAuthentication
      2 def authenticate(self,request) ,记住传request对象
       -如果验证通过,返回None或者两个值
      3 在视图类中使用:(不要加括号)
       authentication_classes=[AuthLogin]

   -认证功能的局部配置
      -authentication_classes=[AuthLogin]
 

  -认证功能的全局配置,在settings.py中配置
      -REST_FRAMEWORK={
       "DEFAULT_AUTHENTICATION_CLASSES":["app01.auth.AuthLogin",]
      }
 

  -全局使用的局部禁用:
      authentication_classes = []
  
   -drf内置了一些认证类(了解):
      -TokenAuthentication
      -SessionAuthentication
 

#BaseAuthentication(规范了接口,模拟其它语言接口的概念)
def authenticate(self, request):
    raise NotImplementedError(".authenticate() must be overridden.")
# 如果写了一个类,继承BaseAuthentication,但是没有重写authenticate,就会抛异常

三、局部钩子源码分析

# 通过is_valid的父级查找到serializers

# 找到该文件下的to_internal_value方法

 # 走完后走run_validation功能

 

原文地址:https://www.cnblogs.com/wuzhengzheng/p/10415114.html