REST-Framework: 认证组件 | token的介绍和使用

目录

一 认证简介

二 认证组件局部使用

三 认证组件全局使用

附:不存数据库的token验证

四 源码分析


一 认证简介

Django 自带一个用户认证系统,这个系统处理用户帐户、组、权限和基于 cookie 的会话.
只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

Rest-Framework中的认证

在restframework中,认证即是通过继承BaseAuthentication重构认证的类,认证的逻辑在类的authenticate方法中实现,通过判断用户传递的信息,如果认证成功返回(用户,用户Token)元组,会将用户对象封装到request里,通过request.用户可以获得用户的相关信息。

二 认证组件局部使用

(1)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,'二笔用户')))

class UserToken(models.Model):
    user=models.OneToOneField(to='User')
    token=models.CharField(max_length=64)

(2)新建认证类(验证通过需要返回return两个参数, 第一个参数实质上给了request.user,  第二个给了request.auth)

from rest_framework.authentication import BaseAuthentication
class TokenAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
        # 这里返回的两个值, 一个通过token对象获取到对应的user对象, 另一个可以任意返回
            return token_obj.user, token
        else:
            raise AuthenticationFailed('认证失败')
    def authenticate_header(self,request):
        pass

(3)view层使用:(不要加括号)

局部使用的时候, 哪个类需要加验证,就在哪个类中加上该属性:  authentication_classes = [TokenAuth, ]

  • 要注意列表中的类,必须是已经写好的用来验证的类,

  • 列表中有多个类的时候, 如果第一个类就有两个返回值, 那么后面的类将不会再继续进行, 是否进行的关键在于验证成功后返回的值是否为空, 为空则继续循环验证类, 反之则停止

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')

三 认证组件全局使用

  • 全局使用的时候会导致一个尴尬的问题就是我们写的登录CBV也需要登录认证才行

  • 全局使用的时候,我们实现部分CBV禁用的方式是,在需要的CBV中写上:authentication_classes = [ ]

  • 当我们写了authentication_classes = [ ] 为空的时候,将不会有返回值,就会让我们进行正常登录

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

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

authentication_classes = [TokenAuth, ]

附:不存数据库的token验证

我们面使用的验证方式算是现在比较主流的验证方式了, 那我们现在来思考一个问题, 上面的验证方式是将token放到数据库中,数量比较少的时候没有什么问题, 但是当并发比较高的时候, 每个用户过来访问服务器的时候,都需要服务器到数据库中去查询进行比对, 这样高强度的查询会给服务器带来很大压力, 那么我们有没有办法来缓解服务器的这种压力的, 在这里我们来介绍一种方法来供大家参考, 这个方法就是不存数据库的token验证

原理: 将token存到客户端来缓解服务器的压力,高并发产生的压力由程序来优化

  • 在程序中首先对用户信息进行加密, 加密格式:(信息加密后的字符串 | 信息字典 ) 把asdfasdf | {name:allen,id:1}  当做token,发到客户端,存储起来

  • 以后客户端再发请求,会携带asdfasdf|{name:lqz,id:1}这个token过来, 服务端在程序中根据分隔符来截取{name:lqz,id:1}

  • 然后再使用相同的加密方式对这些信息进行加密, 加密后得到一串字符串, 然后将两个字符串进行对比, 

  • 两个字符串一样则认证成功, 不一样则认证失败

第二种方式:

我们也可以使用简单粗暴的方式,将信息进行加密后直接发送给客户端, 客户端再次发送请求的时候,使用用一种算法对字符串进行解密, 然后对解密后的信息进行操作

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')

 

四 源码分析

#Request对象的user方法
@property
def user(self):
the authentication classes provided to the request.
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user

def _authenticate(self):
        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()

self.authenticators

    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]

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

你可能感兴趣的:(Rest-Framework,认证组件,Django框架及其组件)