drf之三大认证

一、前言

? 我们知道drf的APIView类的as_view直接对原生django的csrf进行了禁用,是什么让drf有如此底气?从之前对drf的源码分析可以看到,三条语句。

self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)

这就是drf的三大认证。

二、用户认证

1.drf的用户认证

? 我们的某些接口需要对用户进行辨别,那么我们该如何区分A用户和B用户呢?如果A用户想访问B用户的余额,这种操作是不被允许的。在django中,已经完成过基于cookie和session的身份认证,对登录的用户返回一个cookie,并在服务端也进行保存,用户必须携带cookie才能通过用户认证。

drf的认证规则:

  • 如果没有认证信息,则认为是游客
  • 如果认证失败,抛出异常
  • 认证成功返回(user,token)

rest_framework文件下的authentication.py中为我们写好了用户认证的基类,及一些基础的认证类。我们可以通过重写authenticate和相关的方法来定义自己的用户认证类。

class BaseAuthentication:
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass
2.基于token的drf-jwt认证

? 我们借用第三方djangorestframework-jwt来完成我们的用户认证。jwt是通过签发token、校验token来完成用户认证的。token是有 头、体、签名信息组成的一串字符串,以 . 分割开。

'''
token示例eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImN5YW4iLCJleHAiOjE1ODI1NDA0MDcsImVtYWlsIjoiIn0.52E5R00GL0gx-3O3OTosXz0cDWmVzmNU16xEbZpmAkg

1.头和体部一般可以是一个字典序列化成Json格式后转化为二进制然后进行 双向加密如:base64算法得到的。
2.头部信息一般只是对token的所属进行声明,例如项目名称、公司名称
3.体部信息则包含用户的标识信息、及关键信息,如用户的主键、用户的设备信息、token的有效时间、token的签发时间。服务端可以通过对其进行反向解密获得相关信息。
4.尾部信息由 经过加密后的头部和尾部 与 服务器存储的秘钥 进行单向加密如:md5算法生成。

分析:服务端拿到token后会对 头、体及自身存储的秘钥进行md5加密,如果加密的结果与第三段不符,则一定是头、体的信息发生了改变,token便会认为无效。所以服务器端的秘钥是至关重要的,如果泄漏了则攻击者可以伪造任意的token对任意的接口进行访问。
优势:服务端不用再保存用户的认证信息,也就意味着不需要频繁的读写数据库,降低了数据库的压力,在实现服务器集群时也非常方便。
'''

jwtauthentication源码分析

# from rest_framework_jwt.authentication.JSONWebTokenAuthentication类中
class BaseJSONWebTokenAuthentication(BaseAuthentication):
    """
    Token based authentication using the JSON Web Token standard.
    """

    def authenticate(self, request):
        """
        Returns a two-tuple of `User` and token if a valid signature has been
        supplied using JWT-based authentication.  Otherwise returns `None`.
        """
        # 获取request请求中的token
        jwt_value = self.get_jwt_value(request)
        if jwt_value is None:
            return None

        # 对token进碰撞校验:编码格式、过期时间、token是否有效
        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            msg = _('Signature has expired.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = _('Error decoding signature.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()

        # 获取token中包含的user对象
        user = self.authenticate_credentials(payload)

        return (user, jwt_value)

    def authenticate_credentials(self, payload):
        """
        Returns an active user that matches the payload's user id and email.
        """
        User = get_user_model()
        username = jwt_get_username_from_payload(payload)

        if not username:
            msg = _('Invalid payload.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            user = User.objects.get_by_natural_key(username)
        except User.DoesNotExist:
            msg = _('Invalid signature.')
            raise exceptions.AuthenticationFailed(msg)

        if not user.is_active:
            msg = _('User account is disabled.')
            raise exceptions.AuthenticationFailed(msg)

        return user

class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    """
    Clients should authenticate by passing the token key in the "Authorization"
    HTTP header, prepended with the string specified in the setting
    `JWT_AUTH_HEADER_PREFIX`. For example:

        Authorization: JWT eyJhbGciOiAiSFMyNTYiLCAidHlwIj
    """
    www_authenticate_realm = 'api'

    def get_jwt_value(self, request):

        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

        if not auth:
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None

        if smart_text(auth[0].lower()) != auth_header_prefix:
            return None

        # token如果不由 前缀JWT + 原token组成则抛出异常
        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        return auth[1]

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        return '{0} realm="{1}"'.format(api_settings.JWT_AUTH_HEADER_PREFIX, self.www_authenticate_realm)

通过了用户认证的类,request.user的对象要么是Anonymous或者是数据库中合法的user对象,至此,用户认证完毕。

三、权限认证

? 只有通过了用户认证的request请求才会进行权限认证。我们的某些接口通常需要vip用户才能访问,普通用户是无法进行访问的。但vip用户和普通用户都会通过用户认证,我们又该如何区分呢?

rest_framework文件夹下的permissions.py。

class BasePermission(metaclass=BasePermissionMetaclass):
    """
    A base class from which all permission classes should inherit.
    """

    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True
'''
drf自带的权限认证类:
    - AllowAny 允许任何人
    - IsAuthenticated 只允许登录用户
    - IsAdminUser 只允许后台用户
    - IsAutenticatedOrReadOnly  只允许未登录用户读,允许登录用户读写

我们可以通过继承BasePermission类重写has_permission方法来实现自定义的权限认证类,认证通过返回True,否则返回False即可。
'''
校验用户是否是VIP或属于VIP分组的权限类 案例
class IsVipPermission(BasePermission):

    def has_permission(self, request, view):
        if request.user and request.user.is_authenticated and request.user.is_vip:
            return True
        else:
            return False

# 没有is_vip字段,有vip分组控制时
class IsVipPermission(BasePermission):

    def has_permission(self, request, view):
        vip_group = Group.objects.get(name='vip')
        if request.user and request.user.is_authenticated and (vip_group in request.user.groups.all()):
            return True
        else:
            return False

四、频率认证

? 当request请求通过用户认证和权限认证后,还要进行频率的检测,如果我们接口不限制访问频率,那么可能会让攻击者有机可乘,造成服务器的瘫痪。

rest_framework文件夹下的throttling.py中已经定义了基础的频率校验类。我们只需要继承SimpleRateThrottle类并重写get_cache_key方法。

案例:自定义频率类,只限制get请求的访问频率,不限制其他访问请求

class MethodRateThrottle(BaseThrottle):
   scope = 'method'  # scope
   def get_cache_key(self,request,view):
       if request.method.lower() == 'get':
       return self.cache_format % {
           'scope': self.scope,
           'ident': self.get_ident(request)
       }
       else:
           return None
'''
scope需要在settings.py中进行配置
get_cache_key方法,返回None代表 无限制访问,如果返回字符串,则该字符串会在缓冲中被保存(因为数据库中没有相应的表,所以我们推断内存中应该由一张虚拟的表,用来记录访问的频率)。
例如:限制了 3/min的访问频率,如果同一用户在1分钟内访问了3次,则会返回3次相同的字符串(因为该字符串是带有用户标识信息的get_indent,不同的用户的表示信息不同,一个用户被限制不会影响其他用户)。当第4次访问时,reqeust请求就会被拒绝。

'''

五、token刷新

drf-jwt为我们提供了token 的刷新功能,我们可以给token属性设置为可刷新。在token有效并且在刷新过期时间内,可以访问接口来刷新token的过期时间。

"""
1)运用在像12306这样极少数安全性要求高的网站
2)第一个token由登录签发
3)之后的所有正常逻辑,都需要发送两次请求,第一次是刷新token的请求,第二次是正常逻辑的请求
"""
settings.py
import datetime

JWT_AUTH = {
    # 配置过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=5),

    # 是否可刷新
    'JWT_ALLOW_REFRESH': True,
    # 刷新过期时间
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
urls.py
from rest_framework_jwt.views import ObtainJSONWebToken, RefreshJSONWebToken
urlpatterns = [
    url('^login/$', ObtainJSONWebToken.as_view()),  # 登录签发token接口
    url('^refresh/$', RefreshJSONWebToken.as_view()),  # 刷新toekn接口
]

六、多方式登录

drf-jwt只为我们提供了 用户名-密码登录的签发token,是不支持用户以手机号、邮箱登录进行登录的。如果我们想实现多方式登录,必须自定义签发token。

serializers.py
class LoginSerializer(serializers.ModelSerializer):
    # 局部禁用
    authentication_classes = []
    permission_classes = []
    # 需要对字段进行覆盖,以免drf认为这是在做增数据自动校验
    username = serializers.CharField()
    password = serializers.CharField()
    class Meta:
        model = models.User
        fields = ['username', 'password']

    def validate(self, attrs):
        from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
        user = self._get_user(attrs)
        if user:
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            self.context['token'] = token
            return attrs
        else:
            raise exceptions.ValidationError({"error":"username or password valid!"})

    def _get_user(self, attrs):
        import re
        username = attrs.get('username')  # type:str
        password = attrs.get('password')
        if re.match(r'^[email protected]+$', username):
            # 邮箱登录
            print('..邮箱登录')
            user = models.User.objects.filter(email=username).first()
        elif re.match(r'^1[3-9][0-9]{9}$', username):
            print('..手机登录')
            user = models.User.objects.filter(mobile=username).first()
        else:
            user = models.User.objects.filter(username=username).first()
        if user and user.check_password(password):
            return user
urls.py
urlpatterns = [
    url('login/',views.LoginAPIViewSet.as_view({'post':"login"})),
]
views.py
class LoginAPIViewSet(viewsets.GenericViewSet):

    def login(self, request, *args, **kwargs):
        serializer = serializers.LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        token = serializer.context.get('token')
        return Response({'token': token})

认证类的配置

'''
类的优先顺序:局部-->全局-->drf默认
'''
settings.py

JWT_AUTH = {
    # token存活时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
    # token前缀
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
}

REST_FRAMEWORK = {
    # 用户认证
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ],
    # 权限认证
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
    # 频率认证
    'DEFAULT_THROTTLE_CLASSES': [],

    # 频率配置
    'DEFAULT_THROTTLE_RATES': {
        'user': '3/min', # duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        'anon': None,
    },
}

原文地址:https://www.cnblogs.com/Ghostant/p/12363230.html

时间: 2024-10-07 00:28:04

drf之三大认证的相关文章

自定义路由组件,Django的admin后台管理,DRF的三大认证,jwt认证

目录 一.自定义路由组件 1. 为什么要自定义路由组件 2. 自定义路由组件实例 二.Django的admin后台管理 三.DRF的三大认证组件概括 1. 认证组件 2. 权限组件 3. 频率组件 四.Django中的用户权限管理 五.jwt认证 1. jwt认证和普通session认证的区别 2. jwt认证介绍 (1)jwt的原理 (2)jwt三部分的内容 3. jwt的签发算法 (1)第一步:头部算法 (2)第二步:载荷部分的算法 (3)第三步:签名部分的算法 (4)第四步:连接生成tok

drf三大认证

源码分析 dispath方法内 self.initial(request, *args, **kwargs) 进入三大认证 # 认证组件:校验用户(游客,合法用户,非法用户) # 游客:代表校验通过直接进入下一步校验, # 合法用户:代表校验通过,将用户存储在request.user中,在进入下一步校验 # 非法用户:代表校验失败,抛出异常,返回403权限异常结果 self.perform_authentication(request) # 权限组件:校验用户权限,必须登录,所有用户登录读写,游

drf 三大认证详解

目录 drf 三大认证: 认证: 权限: 认证与权限组件绑定使用: 频率: 多方式登录: drf 三大认证: 认证: # 全局配置: -在全局(认证组件只能决定request.user,不是断定权限的地方,所以一般配置全局) REST_FRAMEWORK = { # 认证组件 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication' ], } # 局部禁用(

drf框架 6 视图集与路由组件(开发最常用、最高级) 三大认证原理 RBAC认证规则

准备工作 models.py from django.db import models # 基类:是抽象的(不会完成数据库迁移),目的是提供共有字段的 class BaseModel(models.Model): is_delete = models.BooleanField(default=False) updated_time = models.DateTimeField(auto_now_add=True) class Meta: abstract = True # 必须完成该配置 cla

drf三大认证:认证组件-权限组件-权限六表-自定义认证组件的使用

三大认证工作原理简介 源码分析: from rest_framework.views import APIView 源码分析入口: 内部的三大认证方法封装: 三大组件的原理分析: 权限六表分析 基于用户权限访问控制的认证(RBAC):Role-Based-Access-Control;基于auth的认证规则(了解). Django框架采用的是RBAC认证规则:通常分为:三表规则.五表规则.Django采用的是六表规则. 三表:用户表.角色表.权限表 五表:用户表.角色表.权限表.用户角色关系表.

?DRF?-----三大认证组件--认证组件

认证组件 铺垫: 源码分析 入口: restframework 框架内的 views 下的 APIView 的 dispatch方法 组件的最下面 有三个方法   分别是 认证组件  权限组件 和 频率组件 perform_authentication (认证组件) 校验用户 - 游客 合法用户 非法用户 游客: 代表校验通过 进入下一步校验 (权限校验) 合法用户: 代表校验通过 将用户 存储在 request.user 中 再进行下一步校验 非法用户: 代表校验失败 抛出异常 返回403 权

drf三大认证补充

频率认证 源码分析部分 def check_throttles(self, request): for throttle in self.get_throttles(): if not throttle.allow_request(request, self): self.throttled(request, throttle.wait()) def throttled(self, request, wait): #抛异常,可以自定义异常,实现错误信息的中文显示 raise exceptions

三大认证

一.三大认证功能分析 1)APIView的 dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial(request, *args, **kwargs) 进入三大认证 # 认证组件:校验用户 - 游客.合法用户.非法用户 # 游客:代表校验通过,直接进入下一步校验(权限校验) # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验) # 非法用户:代表校验失败,抛出异常,返回403权

79- drf三大认证的配置及使用方法

目录 三大认证 一.身份认证 1.身份认证配置 2.drf提供的身份认证类(了解) 3.rf-jwt提供的身份认证类(常用) 4.自定义身份认证类(需要自定义签发token时用) 5.自定义签发token及多方式登陆 二.权限认证 1.权限认证配置 2.drf提供的权限认证类 3.自定义权限认证类 三.节流认证(频率认证) 1.节流认证配置 2.drf提供的节流认证类 3.自定义节流认证类 三大认证 一.身份认证 1.身份认证配置 1.1 全局配置身份认证模块 身份认证组件一般都配置在全局set