认证组件

认证组件的局部使用

数据准备:

models:

class User(models.Model):
    username=models.CharField(max_length=64)
    password=models.CharField(max_length=64)
    user_type=models.IntegerField(choices=((0,'普通用户'),(1,'vip用户'),(2,'超级用户')))

#用户token表
class UserToken(models.Model):
    user=models.OneToOneField(to='User')
    token=models.CharField(max_length=64)
View Code

 

认证类的创建:(这个类要放在单独的py文件中,放在views中,无法全局使用,只能局部使用)

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import APIException
class TokenAuth(BaseAuthentication):
    # 源码查看函数名为authenticate,接收两个参数,第二个参数是request对象
    def authenticate(self, request):
        # 写自身的token验证方式
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
           # 返回当前认证的对象,以后访问认证通过后可以从request.user取出用户,注意一旦return,后面的认证类都不会执行了
            return  token_obj.user,token_obj
        else:
            raise APIException('认证失败')
    #继承了BaseAuthentication类 下面这个函数就可以不写了
    def authenticate_header(self,request):
        pass
View Code

 

views:

def get_token(name):
    md = hashlib.md5()
    md.update(name.encode('utf-8'))
    md.update(str(time.time()).encode('utf-8'))
    return md.hexdigest()

class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':100,'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(name)
                models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                back_msg['msg']='登录成功'
                back_msg['token']=token
            else:
                back_msg['status']=101
                back_msg['msg'] = '用户名或密码错误'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)

class Test(APIView):
    authentication_classes = [TokenAuth, ]
    def get(self, request):
        return Response({'name': 'yys'})
View Code

 

总结认证组件:

局部使用:在要认证的类下加上authentication_classes = [TokenAuth, ]   

全局使用:settings中配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.MyAuth.TokenAuth",] }

局部禁用:authentication_classes = [ ]

 

 

token可以存mysql数据库,也可以放在redis中,但是随着用户越来越多,数据库压力会越来越大,下面提供一种不存数据库的token验证方式:

def get_token(name,salt='123'):
    md=hashlib.md5()
    md.update(name.encode('utf-8'))
    md.update(salt.encode('utf-8'))
    return md.hexdigest()+'|'+str(name)

def check_token(token, salt='123'):
    md = hashlib.md5()
    md.update(token.split('|')[1].encode('utf-8'))
    md.update(salt.encode('utf-8'))
    return token.split('|')[0] == md.hexdigest()

class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        success=check_token(token)
        if success:
            # 认证通过返回用户名
            return token.split('|')[1],token.split('|')[0]
        else:
            raise APIException('认证失败')
    def authenticate_header(self,request):
        pass

class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':100,'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(name)              
                back_msg['msg']='登录成功'
                back_msg['token']=token
            else:
                back_msg['status']=101 
                back_msg['msg'] = '用户名或密码错误'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)

class Test(APIView):
    authentication_classes = [TokenAuth, ]
    def get(self, request):
        return Response({'name': 'yys'})
View Code

原理:登录成功后生成 '自身加密方式生成的字符串 | user_name或者user_id' 这种形式的token返给浏览器,下次访问带着token过来,后台取出token中的id或者name以同样的加密方式生成字符串并与浏览器携带过来的token进行验证,如果相同则验证通过。

 

认证组件的源码分析

源码查看流程:

APIView的dispatch方法()  ---- self.initial(request, *args, **kwargs) --- self.perform_authentication(request) --- request.user(Request类下的user方法) --- self._authenticate()

posted on 2018-12-13 15:12  叶杨森  阅读(220)  评论(0)    收藏  举报