• rest_framework之认证与权限 token不存数据库认证


    1. 认证 :

    介绍:

    UserInfo表包含name , pwd , user_type三个字段
    UserToken表包含token与user(关联UserInfo表)
    
    当用户登录成功将随机字符串写入token, 并且将其返回给前台
    
    当用户访问需要登录的视图需要将token拼到url中 , 如
    http://127.0.0.1:8000/books/1?token=fbc4092d-b872-4b23-b2f7-29177ece9208
    在自定义认证类里取到url的token, 与数据库的对比

    源码分析

    APIView---dispatch---initialize_request(包装request)---apiview的get_authenticators---
    ---将对象列表返回给authenticators----执行apiview的initial方法
    ---执行apiview的perform_authentication方法-----(request.user)
    ---Request.user方法----执行request对象的_authenticate方法------
    循环request对象的authenticators----执行循环出的每个对象的authenticate方法,
    将自己和request这个对象传入----最后返回一个元组(认证通过)
    或者抛出异常(认证失败)exceptions.APIException--
    --如果认证通过self.user, self.auth = user_auth_tuple执行的结果解压赋值,给request对象添加二个属性。

    使用实例:

    1. 自定义一个认证类 
    注意:这个类要放在单独的py文件中,(如果放在view中,全局配置无法使用)
    class LoginAuth():
        # 函数名一定要叫authenticate,接收必须两个参数,第二个参数是request对象
        def authenticate(self, request):
            # 从request对象中取出token(也可以从其它地方取)
            token = request.query_params.get('token')
            # 去数据库过滤,查询
            ret = models.UserToken.objects.filter(token=token).first()
            if ret:
                # 能查到,说明认证通过 返回两个值
                # ret.user就是当前登录用户对象,一旦retrun了,后面的认证类都不执行了
                return ret.user,ret
            # 如果查不到,抛异常
            raise exceptions.APIException('您认证失败')
            
    2. 视图类中使用
    - 局部使用
        在视图类中加一行:
            authentication_classes = [LoginAuth, ]
    - 全局使用
        在setting中配置
            REST_FRAMEWORK={
                'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',]
            }
            中括号里的就是能导到认证类的东西
        局部禁用
            在视图类中加一行:
                authentication_classes = []

    2. 权限

    用法介绍

    在自定义权限类里判断用户类型, 如果是3(超级会员)则权限足够, 返回Ture
    如果权限不够返回False

    代码实例

    1. 自定义一个权限类
    class UserPermission():
        # message是出错显示的中文
        message='您没有权限查看'
        def has_permission(self, request, view):
            user_type = request.user.user_type
            if user_type == 3:
                return True
            else:
                return False
    
    2. 使用
    -局部使用
        -在视图类中加一行:
         permission_classes = [UserPermission, ]
    
    -全局使用
        -在setting中配置
        REST_FRAMEWORK={
            'DEFAULT_PERMISSION_CLASSES':['app01.MyAuth.UserPermission',]
        }
        -局部禁用
        -在视图类中加一行:
            -permission_classes = []

    choice

    choice的用法:
        -拿出数字对应的中文:get_字段名_dispaly()

    ====================================

    认证组件的源码分析

    执行到APIVIEW里面的dispatch方法 , 里面有一句
    request = self.initialize_request(request, *args, **kwargs)
    这一句会生成一个request对象, 
        生成这个对象的参数有
        django的request对象, 
        authenticators=self.get_authenticators(),
                这里的结果是authentication_classes列表里面的类实例化出来的一个个对象
    
    然后会执行到self.initial(request, *args, **kwargs)这里
    点进去里面有一句self.perform_authentication(request), 这个就是认证的
    
    --------------------------------------------------------------------
    |       分界线  上面的self代表视图类的对象  下面是Request的对象        |
    --------------------------------------------------------------------
    
    点进去里面调用了request.user, 这是一个包装过的方法, 在rest_framework.request.Request中
    再进去lou一眼,里面有self._authenticate()方法,再点进去
    里面会循环第六行里面的对象列表, 调用每个对象的authenticate(self)方法, 
    
    注意!!!这里其实传了两个参数,
        第一个是对象调用方法自动将自己传过去了,
        第二个参数是request对象
    源码里面会有两个变量来接收他的返回值,所以authenticate里面我们要返回两个值

    总结:

    综上, 所以我们需要在需要认证的视图类里面加上叫做authentication_classes的列表, 列表里面放入我们自定义的认证类

    自定义认证类的时候里面需要加上方法def authenticate(self, request): ,并且返回两个值

    在源码中这个方法被try了, 捕获的异常是exceptions.APIException , 所以如果认证失败抛出APIException这个异常

    认证的token不存到数据库

    每个用户登录都会往数据库里写一条数据,然后每次访问需要登录的地址时都要去数据库检查一下token, 这对数据库的压力比较大

    现提供一种方法不往数据库写token验证

    用户登陆成功以后用自己的方式加密一下用户的id

    我用的是

    "吃葡萄不吐葡萄皮 id 不吃葡萄倒吐葡萄皮"用md5加密,后面再拼上"|id"

    将这个字符串作为token返回给前台

    然后访问其他的需要登录的页面时url里拼上这个就好了

    例如,: id为1的用户登录了, 生成一个7f38e7a645fbd1b3c68fbc75ecd62d24|1字符串

    id为2的用户登录了, 生成一个a07687f76d66557e59db18bd89910e3a|2字符串

    访问其他页面时带上这个字符串,例如访问书本详情时的url为:

    http://127.0.0.1:8000/books/1?token=a07687f76d66557e59db18bd89910e3a|2

    token翻译成明文就是吃葡萄不吐葡萄皮2不吃葡萄倒吐葡萄皮|2

    在认证里面通过 " | " 切分取到id , 再通过上面的加密方式生成token与url里面的进行对比

    代码示例:

    获取加密字符串的函数
    import hashlib
    def get_token(id):
        md5=hashlib.md5()
        md5.update('吃葡萄不吐葡萄皮'.encode('utf-8'))
        md5.update(str(id).encode('utf-8'))
        md5.update('不吃葡萄倒吐葡萄皮'.encode('utf-8'))
        return md5.hexdigest()
    
    登录成功以后生成token:
    token = methord.get_token(user.id)+'|'+str(user.id)
    
    认证类
    class LoginAuth:
        def authenticate(self, request):
            token = request.GET.get('token')#获取url的token
            id = token.split('|')[1]#拿到id
            re_token = methord.get_token(id) + '|' + str(id)#根据我的加密方式进行加密
            if token == re_token:#判断url的token是否正确
                user=models.UserInfo.objects.filter(pk=int(id)).first()
                return user,user
            else:
                raise APIException('还没登录')
  • 相关阅读:
    EF6学习笔记二十五:分布式事务
    EF6学习笔记二十四:事务
    EF6学习笔记二十三:拦截器异常、耗时监控
    EF6学习笔记二十二:初识NLog日志框架
    EF6学习笔记二十一:格式化日志输出
    EF6学习笔记二十:简单日志记录
    EF6学习笔记十九:不一样的复杂类型
    EF6学习笔记十八:DetectChanges
    EF6学习笔记十七:快照追踪与代理追踪性能测试
    EF6学习笔记十六:变更追踪
  • 原文地址:https://www.cnblogs.com/huangxuanya/p/11144021.html
Copyright © 2020-2023  润新知