drf--认证组件

认证简介

使用场景:有些接口在进行访问时,需要确认用户是否已经登录,比如:用户需要购买物品时,在结账的时候,就需要进行登录验证的。

用户认证RBAC(Role-Based Access Control)

一般用户认证都是基于角色认证:

  • 三表机制

    • 用户表(User)
    • 角色表(Group)
    • 权限表(Permission)

    用户表关联角色表,角色表关联权限表

  • 五表机制

    • 用户表(User)
    • 用户角色关系表(User-Group)
    • 角色表(Group)
    • 角色权限表(Group-Permission)
    • 权限表(Permission)

    用户表与角色表多对多、角色表与权限表多对多

  • Django采用六表机制

    • 用户表(User)
    • 用户角色关系表(User-Group)
    • 角色表(Group)
    • 角色权限表(Group-Permission)
    • 权限表(Permission)
    • 角色权限表(User-Permission)

    用户表与角色表多对多、角色表与权限表多对多、用户表和权限表多对多

局部使用

  • models.py
from django.contrib.auth import models

class User(models.Model):
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=32)
    user_type=models.IntegerField(choices=((1,'超级用户'),(2,'普通用户'),(3,'二B用户')))

class UserToken(models.Model):
    user=models.OneToOneField(to='User')
    token=models.CharField(max_length=64)
  • 自定义认证类
from rest_framework.authentication import BaseAuthentication

class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
            return
        else:
            raise AuthenticationFailed('认证失败')
            
    def authenticate_header(self,request):
        pass
  • views.py
def get_random(name):
    import hashlib
    import time
    md=hashlib.md5()
    md.update(bytes(str(time.time()),encoding='utf-8'))
    md.update(bytes(name,encoding='utf-8'))
    return md.hexdigest()


class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':1001,'msg':None}
        try:
            name=reuquest.data.get('name')
            pwd=reuquest.data.get('pwd')
            user=models.User.objects.filter(username=name,password=pwd).first()
            if user:
                token=get_random(name)
                models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                back_msg['status']='1000'
                back_msg['msg']='登录成功'
                back_msg['token']=token
            else:
                back_msg['msg'] = '用户名或密码错误'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)


class Course(APIView):
    authentication_classes = [TokenAuth, ]

    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')
  • 进阶:不存数据库的token验证

    def get_token(id,salt='123'):
        import hashlib
        md=hashlib.md5()
        md.update(bytes(str(id),encoding='utf-8'))
        md.update(bytes(salt,encoding='utf-8'))
        return md.hexdigest()+'|'+str(id)
    
    def check_token(token,salt='123'):
        ll=token.split('|')
        import hashlib
        md=hashlib.md5()
        md.update(bytes(ll[-1],encoding='utf-8'))
        md.update(bytes(salt,encoding='utf-8'))
        if ll[0]==md.hexdigest():
            return True
        else:
            return False
    
    class TokenAuth():
        def authenticate(self, request):
            token = request.GET.get('token')
            success=check_token(token)
            if success:
                return
            else:
                raise AuthenticationFailed('认证失败')
        def authenticate_header(self,request):
            pass
        
    class Login(APIView):
        def post(self,reuquest):
            back_msg={'status':1001,'msg':None}
            try:
                name=reuquest.data.get('name')
                pwd=reuquest.data.get('pwd')
                user=models.User.objects.filter(username=name,password=pwd).first()
                if user:
                    token=get_token(user.pk)
                    # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                    back_msg['status']='1000'
                    back_msg['msg']='登录成功'
                    back_msg['token']=token
                else:
                    back_msg['msg'] = '用户名或密码错误'
            except Exception as e:
                back_msg['msg']=str(e)
            return Response(back_msg)
        
        
    from rest_framework.authentication import BaseAuthentication
    
    class TokenAuth():
        def authenticate(self, request):
            token = request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if token_obj:
                return
            else:
                raise AuthenticationFailed('认证失败')
        def authenticate_header(self,request):
            pass
    
    class Course(APIView):
        authentication_classes = [TokenAuth, ]
    
        def get(self, request):
            return HttpResponse('get')
    
        def post(self, request):
            return HttpResponse('post')
    

总结:局部使用,只需要在视图类里加入:

authentication_classes = [TokenAuth, ]

全局使用

全局使用需要在setting.py中进行配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
}

源码分析

# Request对象的user方法

@property
def user(self):
    """
    Returns the user associated with the current request, as authenticated by the authentication classes provided to the request.
    """
    if not hasattr(self, '_user'):
        with wrap_attributeerrors():
            self._authenticate()
            return self._user

def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
in turn.
    """
    for authenticator in self.authenticators:
        try:
            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()

def _not_authenticated(self):
	"""
	Set authenticator, user & authtoken representing an unauthenticated request.
Defaults are None, AnonymousUser & None.
	"""
    self._authenticator = None
    if api_settings.UNAUTHENTICATED_USER:
        self.user = api_settings.UNAUTHENTICATED_USER()
    else:
        self.user = None

    if api_settings.UNAUTHENTICATED_TOKEN:
        self.auth = api_settings.UNAUTHENTICATED_TOKEN
        ()
    else:
        self.auth = None  
  
# self.authenticators
def get_authenticators(self):
	return [auth() for auth in self.authentication_classes]

认证类使用顺序:先用视图类中的验证类,再用settings里配置的验证类,最后用默认的验证类

posted @ 2019-09-09 10:20  蔚蓝的爱  阅读(124)  评论(0编辑  收藏  举报