zoukankan      html  css  js  c++  java
  • Auth认证

    认证源码分析

    #1、APIAPIView         #进来
    
    #2、APIAPIView类中有很多方法,找到dispatch方法 ,请求来了肯定要走dispatch         
    
        def dispatch(self, request, *args, **kwargs):
            request = self.initialize_request(request, *args, **kwargs)  #request已经变了
            self.initial(request, *args, **kwargs)       #这个request是包装后的
    
    #3、来到initial方法、权限、频率等都在该方法中
         def initial(self, request, *args, **kwargs):    #包装后的request
            self.perform_authentication(request)   #认证
            self.check_permissions(request)
            self.check_throttles(request)
    
    #4、来到perform_authentication,
        def perform_authentication(self, request):
            request.user     #此时就要去包装后的request中找user
    
    #----------------------------------上边都是APIAPIView类中的方法
    #----------------------------------下边看都是Request类中的方法
    #5、点from rest_framework.request import Request进来,找到user方法
        @property
        def user(self):
            self._authenticate()    #执行_authenticate方法
    
    
    #6、来到_authenticate
        def _authenticate(self):
            for authenticator in self.authenticators:   #循环取值,authenticators是什么
                  user_auth_tuple = authenticator.authenticate(self)
    
    #7、它是实例化的时候传过来的,那么实例化是在什么时候完成的?是在第2步request包装的时候完成的,实例化的时候,此时来到第二步,找到request包装的过程
        def __init__(self, request, parsers=None, authenticators=None,
                     negotiator=None, parser_context=None):
            self.authenticators = authenticators or ()
    
    #----------------------------又回到了APIAPIView中
    
    #8、此时来到第二步,找到request包装的过程,点进去,注意此时来到了APIAPIView中
    
        def initialize_request(self, request, *args, **kwargs):
            return Request(
            return Request(
                request,
                parsers=self.get_parsers(), #解析的
                authenticators=self.get_authenticators(), #现在重点关注这,这里的self是谁?是APIAPIView,其实是我们写的类,我们继承了APIAPIView
                negotiator=self.get_content_negotiator(), #分页
                parser_context=parser_context     #解析
    
    #9、自己写的类中没有get_authenticators方法,来它的父类APIAPIView找
        def get_authenticators(self):
            return [auth() for auth in self.authentication_classes]   #直接点进去
    
    #10、同样是APIAPIView中的authentication_classes 
    class APIView(View):
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    
    #11、先不管api_settings是什么,如果我们自己类中写一个authentication_classes,
    #如下  注意:来到了view.py
    class Auth():
        pass
    class Login(APIView):
        authentication_classes=[Auth,]    #那么第9步会从这里拿到Auth,并加括号实例化产生对象
        def get(self,request,*args,**kwargs):
            ret = models.Book.objects.all()
    
    #12、往回看,第8步
    authenticators=self.get_authenticators()
    #authenticators其实就是列表中包含一个一个对象
    #[Auth对象,,,]
    
    
    #--------------------------回到    Request看
    
    #13、看5,6步,先看第5步
            for authenticator in self.authenticators:
            #把[Auth对象,,,]列表中的对象一个一个取出来
                    user_auth_tuple = authenticator.authenticate(self)
                     #调用authenticator 的 authenticate方法,也就是Auth对象的方法,
    
    #所以在自己的Auth方法中把pass去掉,写authenticate方法,,
    #注意注意,这里的authenticate(self)带有self参数,正常情况下,authenticator对象,调用自己的方法,不需要传参,这里传了,这里的self。是谁?是Request对象,所以,我们在写Auth中的authenticate时也需要传参
    
    class Auth():
        def authenticate(self,request):
    View Code

     源码分析

        def _authenticate(self):
    
            for authenticator in self.authenticators:  #已经知道是一个一个对象
                                             #执行authenticate(自己写的)
               user_auth_tuple = authenticator.authenticate(self) 
    
               if user_auth_tuple is not None:     #
                    self._authenticator = authenticator
                    self.user, self.auth = user_auth_tuple    #
                    return
    View Code

     自己手写验证

    class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        pwd = models.CharField(max_length=32)
    
    # 用户token
    class UserToken(models.Model):
        token = models.CharField(max_length=64)
        user = models.OneToOneField(to=UserInfo)
    models.py
    from django.shortcuts import render
    from rest_framework.views import APIView
    from app01 import models
    from django.core.exceptions import ObjectDoesNotExist
    import hashlib
    import time
    from django.http import JsonResponse
    from app01 import MySerializer
    
    
    # Create your views here.
    
    def get_token(name):           #写一个生成token的方法
        # 生成一个md5对象
        md5 = hashlib.md5()
        # 往里添加值,必须是bytes格式
        # time.time()生成时间戳类型,转成字符串,再encode转成bytes格式
        md5.update(str(time.time()).encode('utf-8'))
        md5.update(name.encode('utf-8'))
        return md5.hexdigest()
    
    
    # 登录
    class Login(APIView):
        def post(self, request, *args, **kwargs):
            response = {'status': 100, 'msg': '登录成功'}
            name = request.data.get('name')
            pwd = request.data.get('pwd')
            try:
                user = models.UserInfo.objects.get(name=name, pwd=pwd)
                # 校验通过,登录成功,生成一个随机字符串(身份标识)token
                token = get_token(name)
                # 保存到数据库
                # update_or_create更新或者创建,因为不可能同一用户访问10次,生成10次
                models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
                response['token'] = token
            except ObjectDoesNotExist as e:
                response['status'] = 101
                response['msg'] = '用户名或密码错误'
            except Exception as e:
                response['status'] = 102
                # response['msg']='未知错误'
                response['msg'] = str(e)
            return JsonResponse(response, safe=False)
    views.py
        class Books(APIView):
            def get(self, request, *args, **kwargs):
                response = {'status': 100, 'msg': '查询成功'}
                # 必须登录以后,才能获取数据
                # 取出token,取数据库验证,是否登录
                token = request.query_params.get('token')
                ret = models.UserToken.objects.filter(token=token)
                if ret:
                    # 认证通过,是登录用户
                    ret = models.Book.objects.all()
                    book_ser = MySerializer.BookSerializer(ret, many=True)
                    response['data'] = book_ser.data
                else:
                    response['status'] = 101
                    response['msg'] = '认证不通过'
    
                return JsonResponse(response, safe=False)
    View Code

    使用auth

    1、认证类(在新建的py文件中)

    rom rest_framework.authentication import BaseAuthentication
    class TokenAuth(BaseAuthentication):     #尽量继承,避免抛出异常
        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
    View Code

    2、view层

    def get_random(name):
        import hashlib
        import time
        md=hashlib.md5()
        md.update(bytes(str(time.time()),encoding='utf-8'))
        md.update(bytes(name,encoding='utf-8'))
        return md.hexdigest()
    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_random(name)
                    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)
    
    
    
    class Course(APIView):
        authentication_classes = [TokenAuth, ]
    
        def get(self, request):
            return HttpResponse('get')
    
        def post(self, request):
            return HttpResponse('post')
    View Code

    总结

    #局部使用,只需要在视图类里加入:
    authentication_classes = [TokenAuth, ]

    全局使用

    #全局使用
    #在setting中配置:
    REST_FRAMEWORK={
        'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',]
    }
    app01.MyAuth.LoginAuth认证的地址
    一般情况下,认证不会写在view中,都是单独写在一个py文件中,
    
    #局部禁用,(不可能别人还没有登陆,就需要认证)
    在需要认证的类中,
    authentication_classes = []
    #authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    在读源码的时候,有这么一句,当时的处理是自己写一个authentication_classes 
    #如果步自己写的话,就会用api_settings中的,
    #一直点进来得到,
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication'
    源码

    不存数据库版的token

    #存数据库耗资源,并且需要IO,耗费时间
    def create_token(user_id):
        md5 = hashlib.md5()
        md5.update(user_id.encode('utf-8'))
        md5.update(settings.password.encode('utf-8'))    #加密盐,在settings中写的字符串
        hex = md5.hexdigest()
        token = hex + '|' + user_id
        print(token)
        return token
    
    
    # 登录
    class Login(APIView):
        authentication_classes = []     #局部禁用auth认证,还没登陆,不能验证
    
        def post(self, request, *args, **kwargs):      #发送post请求,
            response = {'status': 100, 'msg': '登录成功'}
            name = request.data.get('name')      
            pwd = request.data.get('pwd')
            try:
                user = models.UserInfo.objects.get(name=name, pwd=pwd)
                user_info_json = json.dumps({'name': user.name, 'id': user.pk})
                # 生产dafgasdewf|{'id':user.pk}的token
                token = create_token(str(user.pk))
                response['token'] = token
            except ObjectDoesNotExist as e:
                response['status'] = 101
                response['msg'] = '用户名或密码错误'
            except Exception as e:
                response['status'] = 102
                # response['msg']='未知错误'
                response['msg'] = str(e)
            return JsonResponse(response, safe=False)
    view.py

    自己写auth认证

    #先看class LoginAuth(BaseAuthentication):
    
           #理念就是,把token取出来,切分后,按照原来的加密凡是加密,判断是否相同
    def check_token(token):   
        ret = True         #两个变量
        user_info=None
        try:
            ll = token.split('|')
            # "eef48b787e24381258aa71d0d53615c2,{"id": 1}"
            md5 = hashlib.md5()     #切分后,把|后边的按照原来的加密方式,进行加密,判断是否与|前边的相同
            md5.update(ll[1].encode('utf-8'))
            md5.update(settings.password.encode('utf-8')) #同样需要加密盐,更安全
            hex = md5.hexdigest()
            if not hex == ll[0]:
                ret = False
            else:
                user_info=ll[1]
        except Exception as e:
            ret = False
        return ret,user_info
    
    
    class LoginAuth(BaseAuthentication):
        # 函数名一定要叫authenticate,接收必须两个参数,第二个参数是request对象
        def authenticate(self, request):
            # 从request对象中取出token(也可以从其它地方取)
            token = request.query_params.get('token')
            ret, user_info = check_token(token)    #拿到token,调用check_token
            if ret:
                return user_info, None
            # 如果查不到,抛异常
            raise exceptions.APIException('您认证失败')    
    MyAuth.py
  • 相关阅读:
    Tomcat/ WebSphere/WebLogic的作用和特点
    Servlet 执行时一般实现哪几个方法?
    synchronized 和 java.util.concurrent.locks.Lock 的异同 ?
    Request 对象的主要方法
    char 型变量中能不能存贮一个中文汉字?为什么?
    描述一下 JVM 加载 class 文 件的原理机制?
    单例设计模式
    Thread和Runnable
    Math.round方法、String实例化
    思路清晰的秘诀:结构化思维(自上而下)
  • 原文地址:https://www.cnblogs.com/pdun/p/11246810.html
Copyright © 2011-2022 走看看