rest_framework之认证组件
一 认证简介
只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件
二 局部使用
(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')
#与user表建一对一的关系
token=models.CharField(max_length=64)
(2)新建认证类(验证通过return两个参数)
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 #这里可以返回数据,第一个在需要认证的函数中为user,第二位auth可以取出
else:
raise AuthenticationFailed('认证失败')
#如果报错可以返回这个异常
def authenticate_header(self,request):
#如果不继承BaseAuthentication类,则需写这个函数
pass
(3)view层
def get_random(name):
'''用来生成hsah值'''
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):
'''验证用户post请求的登陆'''
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)
# 如果用户验证完毕,则存入token
models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
back_msg['status']='1000'
back_msg['msg']='登录成功'
back_msg['token']=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'):
'''用来返回自定义的token值'''
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'):
'''用来验证前台返回的token值'''
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(BaseAuthentication):
'''认证类'''
def authenticate(self, request):
token = request.GET.get('token')
success=check_token(token)
if success:
return
else:
raise AuthenticationFailed('认证失败')
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)
# 无需存入token
# 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')
总结:局部使用,只需要在视图类里加入:
authentication_classes = [TokenAuth, ]
三 全局使用
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
}
#局部不用认证的情况可以设置authentication_classes = []
四 源码分析
#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
#认证成功,可以返回一个元组,但必须是最后一个验证类(authentication_classes)才能返回
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里配置的验证类,最后用默认的验证类