认证组件
认证组件的局部使用
数据准备:
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)
认证类的创建:(这个类要放在单独的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
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'})
总结认证组件:
局部使用:在要认证的类下加上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'})
原理:登录成功后生成 '自身加密方式生成的字符串 | 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()

浙公网安备 33010602011771号