三大認證組件總結

三大認證

入口:dispatch():self.initial(request, *args, **kwargs)#三大認證python

# 認證模塊 校驗用戶是否登錄,登錄用戶,非法用戶,遊客
        self.perform_authentication(request)
        # 權限認證,校驗用戶是否擁有權限,校驗對象是登錄用戶和遊客
        self.check_permissions(request)
        # 頻率認證,訪問接口的次數在設定的時間範圍內是否過快
        # ,配置訪問頻率,每次訪問都要緩存訪問次數,超過必定的次數後須要等待時間。
        self.check_throttles(request)
class APIView(View):

    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    # 三大認證的默認設置
    # 配置視圖類的認證
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    # 配置視圖類的頻率
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    # 配置視圖類的權限

咱們在settings.py中尋找認證和權限的配置算法

而後在咱們本身的settings中進行註冊,如過本身不重寫的話,也能夠不用在本身的settings中進行配置數據庫

REST_FRAMEWORK = {
    # 渲染模塊
    'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer'],

    # 異常模塊
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
    # 認證配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ],
    # 權限配置
    # 認證配置,權限配置是捆綁的,一塊兒出現
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
}

認證模塊的工做原理

  1. 繼承BaseAuthentication類,重寫了authenticate方法
  2. 認證規則(authenticate方法實現體):
    • 沒有攜帶認證信息,直接返回None = >遊客
    • 由認證信息,校驗失敗,拋異常 =》 合法用戶
    • 由認證信息校驗出user對象,合法用戶

源碼

class BasicAuthentication(BaseAuthentication):
    """
    HTTP Basic authentication against username/password.
    """
    www_authenticate_realm = 'api'

    def authenticate(self, request):
        """
        Returns a `User` if a correct username and password have been supplied
        using HTTP Basic authentication.  Otherwise returns `None`.
        """
        auth = get_authorization_header(request).split()
        # print(auth)

        if not auth or auth[0].lower() != b'basic':
            return None

        if len(auth) == 1:
            msg = _('Invalid basic header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid basic header. Credentials string should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
        except (TypeError, UnicodeDecodeError, binascii.Error):
            msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
            raise exceptions.AuthenticationFailed(msg)

        userid, password = auth_parts[0], auth_parts[2]
        return self.authenticate_credentials(userid, password, request)

    def authenticate_credentials(self, userid, password, request=None):
        """
        Authenticate the userid and password against username and password
        with optional request for context.
        """
        credentials = {
            get_user_model().USERNAME_FIELD: userid,
            'password': password
        }
        user = authenticate(request=request, **credentials)
        if user is None:
            raise exceptions.AuthenticationFailed(_('Invalid username/password.'))

        if not user.is_active:
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

        return (user, None)

    def authenticate_header(self, request):
        return 'Basic realm="%s"' % self.www_authenticate_realm

權限認證模塊的工做原理

  1. 繼承BasePermission類,重寫 了has_permission方法
  2. 權限規則(has_permission方法實現體):
    • 返回True,表明由權限
    • 返回Flase,表明五權限

源碼

class AllowAny(BasePermission):
    """
    Allow any access.
    This isn't strictly required, since you could use an empty
    permission_classes list, but it's useful because it makes the intention
    more explicit.
    """

    def has_permission(self, request, view):
        return True  # 認證經過,若是給源碼改爲False,那麼就不會由權限


# 必須認證經過的
class IsAuthenticated(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

自定義認證與權限類

實際開發,系統和第三方提供的認證與權限類已經夠用了,特別特殊的需求才須要自定義django

自定義認證類

"""
認證模塊工做原理
1)繼承BaseAuthentication類,重寫authenticate方法
2)認證規則(authenticate方法實現體):
    沒有攜帶認證信息,直接返回None => 遊客
    有認證信息,校驗失敗,拋異常 => 非法用戶
    有認證信息,校驗出User對象 => 合法用戶
"""

""" 認證類的認證核心規則
def authenticate(self, request):
    token = get_token(request)
    try:
        user = get_user(token)  # 校驗算法
    except:
        raise AuthenticationFailed()
    return (user, token)
"""
# 自定義認證類



from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class TokenAuthentication(BaseAuthentication):
    prefix = 'Token'
    def authenticate(self, request):
        # 拿到前臺的token
        auth = request.META.get('HTTP_AUTHORIZATION')
        # 沒有返回None,有進行校驗
        if not auth:
            return None
        auth_list = auth.split()

        if not (len(auth_list) == 2 and auth_list[0].lower() == self.prefix.lower()):
            raise AuthenticationFailed('非法用戶')

        token = auth_list[1]

        # 校驗算法
        user = _get_obj(token)
        # 校驗失敗拋異常,成功返回(user, token)
        return (user, token)

校驗算法

# 校驗算法(認證類)與簽發算法配套
"""
拆封token:一段 二段 三段
用戶名:b64decode(一段)
用戶主鍵:b64decode(二段)
碰撞解密:md5(用戶名+用戶主鍵+服務器祕鑰) == 三段
"""
import base64, json, hashlib
from django.conf import settings
from api.models import User
def _get_obj(token):
    token_list = token.split('.')
    if len(token_list) != 3:
        raise AuthenticationFailed('token異常')
    username = json.loads(base64.b64decode(token_list[0])).get('username')
    pk = json.loads(base64.b64decode(token_list[1])).get('pk')

    md5_dic = {
        'username': username,
        'pk': pk,
        'key': settings.SECRET_KEY
    }

    if token_list[2] != hashlib.md5(json.dumps(md5_dic).encode()).hexdigest():
        raise AuthenticationFailed('token內容異常')

    user_obj = User.objects.get(pk=pk, username=username)
    return user_obj

自定義權限類

# 自定義權限類

"""
權限模塊工做原理
1)繼承BasePermission類,重寫has_permission方法
2)權限規則(has_permission方法實現體):
    返回True,表明有權限
    返回False,表明無權限
"""
from rest_framework.permissions import BasePermission
class SuperUserPermission(BasePermission):
    def has_permission(self, request, view):
        # print(request.user)
        # print(request.auth)

        return request.user and request.user.is_superuser#肯定那些用戶有訪問權限

限制登錄才能訪問

查看全部用戶信息,前提:必須是登陸的超級管理員json

# 登陸接口:若是是超級管理員登陸,返回一個能夠交易出超級管理員的token字符串
# 只要有用戶登陸,就能夠返回一個與登陸用戶相關的token字符串 => 返回給前臺 => 簽發token => user_obj -> token_str

from rest_framework.generics import GenericAPIView
class LoginAPIView(APIView):
    # 登陸接口必定要作:局部禁用 認證 與 權限 校驗
    authentication_classes = []
    permission_classes = []
    def post(self, request, *args, **kwargs):
        serializer = serializers.LoginModelSerializer(data=request.data)
        # 重點:校驗成功後,就能夠返回信息,必定不能調用save方法,由於該post方法只完成數據庫查操做
        # 因此校驗會獲得user對象,而且在校驗過程當中,會完成token簽發(user_obj -> token_str)
        serializer.is_valid(raise_exception=True)
        return APIResponse(data={
            'username': serializer.user.username,
            'token': serializer.token
        })
from django.contrib.auth import authenticate
class LoginModelSerializer(ModelSerializer):
    # username和password字段默認會走系統校驗,而系統的post請求校驗,必定當作增方式校驗,因此用戶名會出現 重複 的異常
    # 因此自定義兩個字段接收前臺的帳號密碼
    usr = serializers.CharField(write_only=True)
    pwd = serializers.CharField(write_only=True)
    class Meta:
        model = models.User
        fields = ('usr', 'pwd')
    def validate(self, attrs):
        usr = attrs.get('usr')
        pwd = attrs.get('pwd')
        try:
            user_obj = authenticate(username=usr, password=pwd)
        except:
            raise ValidationError({'user': '提供的用戶信息有誤'})

        # 拓展名稱空間
        self.user = user_obj
        # 簽發token
        self.token = _get_token(user_obj)

        return attrs

自定義簽發token

# 自定義簽發token
# 分析:拿user獲得token,後期還須要經過token獲得user
# token:用戶名(base64加密).用戶主鍵(base64加密).用戶名+用戶主鍵+服務器祕鑰(md5加密)
# eg: YWJj.Ao12bd.2c953ca5144a6c0a187a264ef08e1af1

# 簽發算法:b64encode(用戶名).b64encode(用戶主鍵).md5(用戶名+用戶主鍵+服務器祕鑰)
# 校驗算法(認證類)與簽發算法配套
"""
拆封token:一段 二段 三段
用戶名:b64decode(一段)
用戶主鍵:b64decode(二段)
碰撞解密:md5(用戶名+用戶主鍵+服務器祕鑰) == 三段
"""
def _get_token(obj):
    import base64, json, hashlib
    from django.conf import settings
    t1 = base64.b64encode(json.dumps({'username': obj.username}).encode()).decode()
    t2 = base64.b64encode(json.dumps({'pk': obj.id}).encode()).decode()
    t3_json = json.dumps({
        'username': obj.username,
        'pk': obj.id,
        'key': settings.SECRET_KEY
    })
    t3 = hashlib.md5(t3_json.encode()).hexdigest()
    return '%s.%s.%s' % (t1, t2, t3)

頻率認證

返回我的中心:所有信息

局部配置

# 頻率模塊局部配置throttle_classes = [ThreeTimeUserThrottle]api

from rest_framework.permissions import IsAuthenticated
from utils.throttles import ThreeTimeUserThrottle
class UserCenterAPIView(APIView):
    # 認證全局配置嗎,權限局部配置
    # authentication_classes = []
    permission_classes = [IsAuthenticated]
    # 頻率模塊局部配置
    throttle_classes = [ThreeTimeUserThrottle]

    def get(self, request, *args, **kwargs):
        user = request.user
        serializer = serializers.UserModelSerializer(user)
        return APIResponse(data=serializer.data)
# @Author : OceanSkychen # @File : serializers.py
# 只參與序列化
class UserModelSerializer(ModelSerializer):
    # 改了原數據庫字段的序列化方式
    password = SerializerMethodField()
    def get_password(self, obj):
        return '########'  #設置密碼返回爲###########

    class Meta:
        model = models.User
        fields = ('username', 'password', 'mobile', 'email', 'first_name', 'last_name')

自定義頻率類

1)定義類繼承SimpleRateThrottle,重寫get_cache_key方法,設置scope類屬性
2)scope就是一個認證字符串,在配置文件中配置scope字符串對應的頻率設置
3)get_cache_key的返回值是字符串,該字符串是緩存訪問次數的緩存key緩存

# @Author : OceanSkychen # @File : throttles.py

from rest_framework.throttling import SimpleRateThrottle
# 1)定義類繼承SimpleRateThrottle,重寫get_cache_key方法,設置scope類屬性
# 2)scope就是一個認證字符串,在配置文件中配置scope字符串對應的頻率設置
# 3)get_cache_key的返回值是字符串,該字符串是緩存訪問次數的緩存key
class ThreeTimeUserThrottle(SimpleRateThrottle):
    scope = 'three'
    # 當前用戶緩存的key
    def get_cache_key(self, request, view):
        return 'throttle:user_%s' % (request.user.id)

全局配置:settings.py

# drf的配置
REST_FRAMEWORK = {
    # 異常模塊
    'EXCEPTION_HANDLER': 'utils.exception.exception_handler',

    # 認證模塊
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # jwt認證類
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ],
    # 權限模塊
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.IsAuthenticated',
        # 自定義權限類
    ],
    # 頻率設置
    'DEFAULT_THROTTLE_RATES': {
        'three': '3/min',
    },
}

先後臺分離模式下信息交互規則

"""
1)任何人都能直接訪問的接口
    請求不是是get、仍是post等,不須要作任何校驗

2)必須登陸後才能訪問的接口
    任何請求方式均可能作該方式的限制,請求必須在請求頭中攜帶認證信息 - authorization
    
3)前臺的認證信息獲取只能經過登陸接口
    前臺提供帳號密碼等信息,去後臺換認證信息token
    
4)前臺如何完成登陸註銷
    前臺登陸成功通常在cookie中保存認證信息token,分離註銷就是前臺主動清除保存的token信息
"""

相關文章
相關標籤/搜索