DRF之认证

认证流程源码分析

认证流程分析

def dispatch(self, request, *args, **kwargs):
    # step 1
    request = self.initialize_request(request, *args, **kwargs)
    
    try:
        self.initial(request, *args, **kwargs)


# step 2
def initialize_request(self, request, *args, **kwargs):
    return Request(
        request,
        authenticators=self.get_authenticators(),
    )
    

# 2.1、Request类
self.authenticators = authenticators or ()


# step 3:获取auth认证类实例化对象列表
def get_authenticators(self):
    return [auth() for auth in self.authentication_classes]
    

class APIView(View):
    # 默认的认证类,可以在类中定义此变量
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    

#  step 4
try:
    self.initial(request, *args, **kwargs)

#  step 5
def initial(self, request, *args, **kwargs):
    self.perform_authentication(request)
    

#  step 6:
def perform_authentication(self, request):
    request.user

#  step 7
class Request(object):
    def __init__(self, request, authenticators=None):
        self.authenticators = authenticators or ()

    @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
            
        self._authenticate()  # 调用
        return self._user

    #  step 8:读取认证对象列表
    def _authenticate(self):
    
        for authenticator in self.authenticators:
            try:
                # 对每个进行验证,异常则全部终止,若返回None,则继续循环
                user_auth_tuple = authenticator.authenticate(self)
                
            except exceptions.APIException:
                self._not_authenticated()
                raise
    
            if user_auth_tuple is not None:
                # 赋值
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                # 通过直接退出循环
                return
        
        # 全都没有通过则设置匿名用户
        self._not_authenticated()

获取默认的认证类

from rest_framework.views import APIView
from django.shortcuts import HttpResponse


class UserView(APIView):
    def get(self,request,*args,**kwargs):
        self.dispatch
        print(request.version)
        print(self.authentication_classes)   # self为 UserView 实例对象
        '''
        # 默认认证类:[
            rest_framework.authentication.SessionAuthentication,
            rest_framework.authentication.BasicAuthentication
        ]
        '''
        return HttpResponse('....')

默认的认证类代码

# 默认的认证类
from rest_framework.authentication import SessionAuthentication,BasicAuthentication

class SessionAuthentication(BaseAuthentication):
     def authenticate(self, request):
        user = getattr(request._request, 'user', None)
        if not user or not user.is_active:
            return None
        self.enforce_csrf(request)
        return (user, None)


class BasicAuthentication(BaseAuthentication):
    def authenticate(self, request):
        return (user, None)

自定义认证规则

示例:

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions


USERTOKEN_LIST = [
    'rqwtashash',
    'cvjkdfdfod'
]


class MyAuthentication(BaseAuthentication):

    def authenticate(self, request):
        # 以url传递认证信息
        tk = request._request.GET.get('tk')  # 获取认证信息
        if tk in USERTOKEN_LIST:
            return ('kate',None)
        
            # return None  # 返回匿名用户
        else:
            # 自定义认证失败信息
            raise exceptions.AuthenticationFailed('认证失败!')


class UserView(APIView):

    # 局部指定认证类
    authentication_classes = [MyAuthentication,]  # 优先当在前类取此认证类

修改默认匿名用户为None:

# 若没有指定,则登录失败默认request.user=AnonymousUser

# settings.py
REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER":None,  # 登录失败则用户为None
    'UNAUTHENTICATED_TOKEN':None,
}

利用继承关系定制继承类:

class Auth(object):
    """用来继承使用自定制的类"""
    authentication_classes = [CustomAuthentication, ]


class LoginView(Auth, APIView):
    # 在LoginView示例再认证时会优先进入Auth类搜索authentication_classes变量
    pass

认证扩展之token认证

1、创建表:

from django.db import models


class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    email = models.CharField(max_length=64)


class Token(models.Model):
    value = models.CharField(max_length=64)  # session_key
    user = models.OneToOneField(UserInfo)  # 只能登陆一次,再次登陆则会重新生成token值
    
    
# 迁移
makemigrations
migrate

2、路由配置:

from django.conf.urls import url
from app01.views import LoginView

urlpatterns = [
    url(r'^login/', LoginView.as_view()),
]

3、视图:

def get_token(username):
    """生成token值"""
    import time
    import hashlib
    hash = hashlib.md5(username.encode('utf-8'))
    hash.update(str(time.time()).encode('utf-8'))
    return hash.hexdigest()


class LoginView(APIView):
    
    def post(self,request,*args,**kwargs):
        """根据用户提交的验证信息,若通过验证,则生成或者更新对应的token值"""
        username = request.data.get('username')
        password = request.data.get('password')
        ret = {'code':1000,'msg':None}
        from .models import UserInfo,Token
        user = UserInfo.objects.filter(username=username,password=password).first()
        if user:
            value = get_token(username)
            # 根据user查询,若没找到则创建,找到则更新defaults内容
            Token.objects.update_or_create(user=user,defaults={'value':value})
            ret['code'] = 1001
            ret['token'] = value

        else:
            ret['msg'] = '用户名或密码错误'

        return JsonResponse(ret)

3、更新或创建token测试:

# 先安装requests模块
pip install requests


import requests

response = requests.post(
    url='http://127.0.0.1:8000/login/',
    data={'username':'alex1','password':'abc123'}
)
print(response.text)

# 登陆错误 {"code": 1000, "msg": "\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef"}
# 登陆成功 {"code": 1001, "msg": null, "token": "9c4cb1631b3a5ead33fb09f6349c4bc7"}

4、设置认证类:

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions


class MyAuthentication(BaseAuthentication):

    def authenticate(self, request):
        # 以url传递认证信息
        tk = request._request.GET.get('tk')  # 获取认证信息
        from app01.models import Token
        token_obj = Token.objects.filter(value=tk).first()
        if token_obj:
            return (token_obj.user, token_obj)
        else:
            raise exceptions.AuthenticationFailed('认证失败!')

5、利用继承指定使用的认证类:

class Auth(object):

    # 优先当在前类取此变量
    authentication_classes = [MyAuthentication,]

6、视图:

class UserView(Auth,APIView):
    #  在调用get函数会调用Auth中指定的认证类进行相关验证
    def get(self,request,*args,**kwargs):
        print(request.user)  # 直接取出用户对象
        return HttpResponse('....')

7、路由:

from django.conf.urls import url
from app01.views import UserView

urlpatterns = [
    url(r'^users/', UserView.as_view()),

8、测试:

# 浏览器输入:http://127.0.0.1:8000/users/?tk=value
# 只有携带符合条件的tk值才会通过认证,进入相应的视图

猜你喜欢

转载自www.cnblogs.com/fqh202/p/9396596.html