• DRF认证、权限和限流


    一、认证

    这里只记录JWT的认证方式:

    1. 首先针对用户需要创建JWT的token,方式如下:

    # jwt_auth.py
    import
    jwt,datetime from django.conf import settings def create_token(payload,timeout=30): # JWT包含header、payload和hash签名,其中header可以自己写,也可以用默认的。有这三个元素就可以生成token。 header = { "alg": "HS256", "typ": "JWT" } payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout) salt = settings.SECRET_KEY token = jwt.encode(payload=payload, key= salt, headers=header, algorithm='HS256').decode('utf-8') return token

    2. 然后用户请求的时候,需要将这个token传递给对方:

    # views.py
    class
    Login(APIView): # 登陆的试图不启用认证,这里改写认证类 authentication_classes = [] def post(self, request): # 获取用户的账号和密码 user = request.data.get('username') pwd = request.data.get('password') #校验用户的账号和密码,验证通过就构造一个payload,这里将IP地址也作为一个认证项 if user == 'zhangsan' and pwd == '123456': payload = { 'user_id': 1, 'username': 'zhangsan', 'user_ip': request.META.get('REMOTE_ADDR'), # 获取客户端ip地址 } # 获取token token = create_token(payload) # 给终端返还token return JsonResponse({'code':0, 'data':token}) else: return JsonResponse({'code':1, 'data':'login failed.'})

    3. 用户获取到Token之后,后续访问需要携带Token,我们需要验证Token是否非法,所以需要写一个验证函数:

    # jwt_auth.py
    from
    rest_framework.authentication import BaseAuthentication import jwt from django.conf import settings from rest_framework.exceptions import AuthenticationFailed class JwtQueryParamsBaseAuthentication(BaseAuthentication): def authenticate(self, request): salt = settings.SECRET_KEY token = request.META.get('HTTP_TOKEN') # 若抛出异常,后续代码将不执行 try: payload = jwt.decode(token,salt,True) if payload['user_ip'] != request.META.get('REMOTE_ADDR'): # 判断token里面的ip地址和客户端IP是否相同 raise AuthenticationFailed({'code':1, 'error': "IP address invalid."}) except jwt.exceptions.ExpiredSignatureError: raise AuthenticationFailed({'code':1, 'error':'The token has expired.'}) except jwt.DecodeError: raise AuthenticationFailed({'code':1, 'error':'Authentication failed.'}) except jwt.InvalidTokenError: raise AuthenticationFailed({'code':1, 'error':'token invalid.'}) return (payload['username'], token) # 抛出异常,后续不再执行 # return一个元组(1,2),认证通过,在试图中如果调用request.user就是元组的第一个值,request.auth就是元组的第二值

    4. 用户发起访问,为了让用户使用认证,这里将所有视图缺省使用认证(在setting里面加),单个不使用认证的就单个视图中改写认证函数:

    # sittings.py
    REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'netapps.extensions.auth.JwtQueryParamsBaseAuthentication', # 调用我们自己写的认证验证 ], }

    5. 这样用户发起访问的时候,需要走认证流程。

    # views.py
    class
    Logs(APIView): def get(self,request): print(request.user) # 'zhangsan' return HttpResponse("订单列表")

    二、权限控制

    1. 为了控制用户访问权限,要求只有张三能访问,其他人不能访问。这里写一个权限函数:

    #permission.py
    from rest_framework.permissions import BasePermission
    
    class MyPermission(BasePermission):
    
        def has_permission(self,request,view):
            # 这个函数返回True或者False,True认证通过,False不通过
            if request.user != 'zhangsan':
                return False
            return True

    2. 用户发起访问,为了让用户使用权限,这里将所有视图缺省使用权限控制(在setting里面加),单个不使用权限控制的就单个视图中改写权限函数:

    # sittings.py
    REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'netapps.extensions.permission.MyPermission' # 调用我们自己写的权限验证 ], }

    3. 用户获取Token的时候,需要取消权限控制,所以在login这个函数里面需要改写权限函数:

    # views.py
    class
    Login(APIView): authentication_classes = [] # 改写认证函数 permission_classes = [] # 改写权限函数 def post(self, request): # 获取用户的账号和密码 user = request.data.get('username') pwd = request.data.get('password') #校验用户的账号和密码,验证通过就构造一个payload,这里将IP地址也作为一个认证项 if user == 'zhangsan' and pwd == '123456': payload = { 'user_id': 1, 'username': 'zhangsan', 'user_ip': request.META.get('REMOTE_ADDR'), # 获取客户端ip地址 } # 获取token token = create_token(payload) # 给终端返还token return JsonResponse({'code':0, 'data':token}) else: return JsonResponse({'code':1, 'data':'login failed.'})

    三、限流

    1. 这里我们针对限流写两个限流函数,一个是限制用户获取Token的频率为每分钟10次,一个是限制用户访问业务的频率为每秒5次。

    # throttle.py
    from rest_framework.throttling import SimpleRateThrottle
    
    class AllThrottle(SimpleRateThrottle):
        # 全局限流函数,基于用户限流,每秒5次
        scope = 'throttle'  # 该字段用来获取限流的频率,例如'5/s'表示每秒5次,在sitting里面设置
    
        def get_cache_key(self, request, view):
            return request.user  # 该字段用来定义基于什么做限流,这里基于用户做限流
    
    class TokenThrottle(SimpleRateThrottle):
        scope = 'Token_throttle'  # 该字段是设置获取Token的频率
    
        def get_cache_key(self, request, view):
            return self.get_ident(request)  # 基于IP地址做限流

    2. 在设置里面去配置限流:

    # settings.py
    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'netapps.extensions.permission.MyPermission'  # 调用我们自己写的权限验证
            
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'netapps.extensions.auth.JwtQueryParamsBaseAuthentication',  # 调用我们自己写的认证验证
        ],
        'DEFAULT_THROTTLE_CLASSES':[
            'netapps.extensions.throttle.AllThrottle'  # 设置全局使用的限流策略
        ],
        'DEFAULT_THROTTLE_RATES':{
            'throttle': '5/s',  # 跟throttle.py中的scope对应,一秒5次
            'Token_throttle': '10/m'  # 一分钟10次
        }
    }

    3. 由于全局使用的是AllThrottle这个函数,都是一秒5次,针对获取Token的频率我们需要改写限流函数:

    # views.py
    from netapps.extensions.throttle import TokenThrottle  # 引入Token限流函数
    # Create your views here.
    
    class Login(APIView):
        authentication_classes = []
        permission_classes = []
        throttle_classes = [TokenThrottle,]  # 调用
        def post(self, request):
            # 获取用户的账号和密码
            user = request.data.get('username')
            pwd = request.data.get('password')
            #校验用户的账号和密码,验证通过就构造一个payload,这里将IP地址也作为一个认证项
            if user == 'zhangsan' and pwd == '123456':
                payload = {
                    'user_id': 1,
                    'username': 'zhangsan',
                    'user_ip': request.META.get('REMOTE_ADDR'),  # 获取客户端ip地址
                }
                # 获取token
                token = create_token(payload)
                # 给终端返还token
                return JsonResponse({'code':0, 'data':token})  
            else:
                return JsonResponse({'code':1, 'data':'login failed.'})  

    这样就完成了,认证、权限控制和限流了。

     四、使用数据库用户进行认证授权

    可以看到上面的认证都是张三这个人,可以同步数据库。在数据库中,有一张系统自带的名为auth_user的表,用于存储用户信息。该表通过密文方式存储密码,创建用户的两种方式:

    1. 创建超级用户

    [root@localhost myproject]# python manage.py createsuperuser
    Username (leave blank to use 'root'): lisi
    Email address: lisi@qq.com
    Password: 
    Password (again): 
    Superuser created successfully.

    2. 创建普通用户

    python manage.py shell  # 进入python bash视图
    
    from django.contrib.auth.models import User  # 导入用户表
    
    User.objects.create_user('wangwu', 'wangwu@qq.com', 'password123')  # 顺序为用户名、邮箱、密码

    接下来我们将通过数据库来完成用户的认证:

    # views.py
    from
    django.http.response import JsonResponsefrom rest_framework.views import APIView from netapps.extensions.jwt_auth import create_token from netapps.extensions.throttle import TokenThrottle from django.contrib import auth from django.contrib.auth.models import User # Create your views here. class Login(APIView): authentication_classes = [] permission_classes = [] throttle_classes = [TokenThrottle,] def post(self, request): # 获取用户的账号和密码 user = request.data.get('username') pwd = request.data.get('password') #校验用户的账号和密码,验证通过就构造一个payload,这里将IP地址也作为一个认证项 user = auth.authenticate(username=user,password = pwd) # 将用户名和密码跟数据库比较,若账号密码正确则返回用户名,若不正确则返回None if user: payload = { 'user_id': 1, 'username': str(user), # 将用户传递给payload,由于用户是<class 'django.contrib.auth.models.User'>类型,需要强转str 'user_ip': request.META.get('REMOTE_ADDR'), # 获取客户端ip地址 } # 获取token token = create_token(payload) # 给终端返还token return JsonResponse({'code':0, 'Token':token}) else: return JsonResponse({'code':1, 'msg':'login failed.'})

    然后在权限控制里面,也可以拉取数据库的用户。

    # permission.py
    from rest_framework.permissions import BasePermission
    from django.contrib.auth.models import User
    
    class MyPermission(BasePermission):
    
        def has_permission(self,request,view):
            # 这里只要用户在数据库能查询到,就返回True,也可以基于实际场景对不同用户做授权。
            if User.objects.filter(username = request.user['username']):
                return True
            return False

    五、为什么要使用auth_user来做用户认证

    auth_user是一个系统自带的表,该表实现了密码加密的功能,也就是存储在数据库的密码是一个hash值,这样更安全。

    这个密码加密的实现方式是通过两个函数来实现的:

    from django.contrib.auth.hashers import make_password, check_password
        def make_password(password, salt=None, hasher='default'):
            """
            将纯文本密码转换为用于数据库存储的哈希值。需要三个参数:
            password: 密码
            salt: 加盐(若不传值,将会使用随机值)
            hasher: hash算法
            若密码为空,将返回UNUSABLE_PASSWORD_PREFIX 和随机字符串的hash值,这将禁止用户登陆。
            """
            pass
        
        def check_password(password, encoded, setter=None, preferred='default'):
            """
            返回一个bool类型,判断密码是否正确。
            至少传2个参数,一个是密码,一个是hash值
            如果指定了setter,它会在需要重新生成密码时调用。
            """
  • 相关阅读:
    jQuery的简单函数
    Playwright-录制脚本进行自动化测试
    使用requests爬取图片并下载
    使用jmeter对websocket进行性能测试
    selenium定位动态元素的2种情况
    Python-Faker
    关于css中@media的基本使用方法
    selenium-浏览器窗口最大化、刷新、前进、后退
    selenium-滚动条滑动,iframe切换,切换窗口
    jmeter-阶梯式压测
  • 原文地址:https://www.cnblogs.com/tortoise512/p/15227715.html
Copyright © 2020-2023  润新知