DAY99 - Rest Framework(四)- 認證元件和許可權元件
阿新 • • 發佈:2018-12-13
一、認證元件
1.使用
# 模型層
class User(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
class UserToken(models.Model):
user = models.OneToOneField(to='User')
token = models.CharField(max_length=64)
#myAuth.py from app01 import models from rest_framework.exceptions import APIException from rest_framework.authentication import BaseAuthentication class Auth(BaseAuthentication): def authenticate(self,request): # 包裝後的request物件,請求來的所有東西都能拿出來 token = request.GET.get('token') ret = models.UserToken.objects.filter(token=token).first() # 如果有值,說明登入過了,而且帶的隨機字串也是正確的 # 如果認證通過,需要返回東西,如果認證不通過,要拋異常 if ret: # 返回一個元組如果返回None,就會是一個匿名使用者 return ret.user,ret else: # 如果沒有值,拋異常 raise APIException('您沒有登入')
from app01.mySer import * import uuid # 登陸檢視 class Login(APIView): def post(self, request): response = {'status': 200, 'msg': '登入成功'} name = request.data.get('name') pwd = request.data.get('pwd') user = models.User.objects.filter(name=name, pwd=pwd).first() if user: token = uuid.uuid4() # 登陸成功後,存入token表 models.UserToken.objects.create(token=token, user=user) response['token'] = token else: response['status'] = 201 response['msg'] = '新增失敗' return JsonResponse(response, safe=False)
# 區域性使用,在所需的視圖裡區域性使用 class Books(APIView): # 登入後才能操作 authentication_classes=[Auth,] def get(self, request): ....... def post(self, request): ....... class BooksDetail(APIView): authentication_classes = [Auth, ] def get(self, request): ....... def post(self, request): ....... # 全域性使用 # setting.py REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.my_Auth.Auth'] } # 全域性使用後區域性禁用 # 在所屬的試圖裡寫一個空 authentication_classes=[]
2.原始碼分析
# 第一步
# APIView類
def dispatch(self, request, *args, **kwargs):
........
# 重點是這個,這是認證、頻率相關的
self.initial(request, *args, **kwargs)
........
# 第二步
# APIView類
def initial(self, request, *args, **kwargs):
........
# 這個就是認證
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
# 第三步
# APIView類
def perform_authentication(self, request):
# 這個request是已經封裝好後的Request的物件
request.user
# 第四步
# Request類
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
# 第五步
# Request類
def _authenticate(self):
# 從下面回到這裡,就可以知道
# self.authenticators=authentication_classes
# 拿上面的例子舉例
# self.authenticators=[Auth, ];authenticator就是Auth
for authenticator in self.authenticators:
try:
# 注意:authenticator.authenticate(self)中的self,由於是在Request類裡,所以這個self就是Request例項化的物件request;
# 所以:authenticator.authenticate(self)=Auth.authenticate(self,request)
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
# user_auth_tuple:authenticator.authenticate(self)的返回值,是個元組
# 這就是一個元組的解壓
# self.user:request.user
# self.auth:request.auth
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
# self.authenticators的來歷
# APIView類
# self.authenticators 是Request例項化的時候傳進來的引數self.get_authenticators()
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# self.get_authenticators()的來歷
# APIView類
def get_authenticators(self):
# self.authentication_classes
# 從子類找驗證類:authentication_classes = [Auth, ]
# 從父類APIView裡找驗證類:authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
# 找到之後,迴圈並加上()執行
return [auth() for auth in self.authentication_classes]
3.不依靠資料庫的token驗證
import hashlib
# 設定token
def set_token(id, salt='1'):
md5 = hashlib.md5()
md5.update(str(id).encode('utf-8'))
md5.update(salt.encode('utf-8'))
return md5.hexdigest() + '|' + str(id)
# 校驗token
def check_token(token, salt='1'):
tk = token.split('|')
md5 = hashlib.md5()
md5.update(str(tk[-1]).encode('utf-8'))
md5.update(salt.encode('utf-8'))
if tk[0] == md5.hexdigest():
return True
else:
return False
class Auth(BaseAuthentication):
def authenticate(self, request):
token = request.GET.get('token')
# 如果GET裡沒有token,check_token裡的split方法就會報錯
# 所以這裡try一下異常
try:
ret = common.check_token(token)
if ret:
return
else:
# 拋異常
raise
except Exception as e:
raise AuthenticationFailed('認證失敗')
class Login(APIView):
def post(self, request):
response = {'status': 100, 'msg': '登陸成功', 'token': None}
name = request.data.get('name')
pwd = request.data.get('pwd')
user = models.User.objects.filter(name=name, pwd=pwd).first()
if user:
# 傳入使用者id獲得對應的token
token = common.set_token(user.pk)
response['token'] = token
else:
response['status'] = 200
response['msg'] = '登入失敗'
return JsonResponse(response, safe=False)
class Book(APIView):
authentication_classes = [myAuthentication.Auth, ]
def get(self, request):
response = {'status': 100, 'msg': '檢視成功', 'data': None}
books = models.Book.objects.all()
ret = mySerializers.BookSerializers(books,many=True)
response['data']=ret.data
return JsonResponse(response,safe=False)
4.順序
認證類使用順序:先用檢視類中的驗證類,再用settings裡配置的驗證類,最後用預設的驗證類
二、許可權元件
1.簡單使用
from rest_framework.permissions import BasePermission
class VipPermission(BasePermission):
message = '不是會員,檢視不了'
def has_permission(self, request, view):
user_type = request.user.user_type
if user_type == '1':
return True
else:
return False
class Book(APIView):
# 區域性使用
permission_classes = [myPermission.VipPermission, ]
def get(self, request):
response = {'status': 100, 'msg': '檢視成功', 'data': None}
books = models.Book.objects.all()
ret = mySerializers.BookSerializers(books, many=True)
response['data'] = ret.data
return JsonResponse(response, safe=False)
# 全域性使用
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES':['app01.myPermission.VipPermission']
}
# 全域性使用中區域性禁用
class Book(APIView):
permission_classes = []
2.原始碼分析
# 第一步
# APIView類
def dispatch(self, request, *args, **kwargs):
........
# 重點是這個,這是認證、頻率以及許可權相關的
self.initial(request, *args, **kwargs)
........
# 第二步
# APIView類
def initial(self, request, *args, **kwargs):
........
self.perform_authentication(request)
# 這就是 許可權方法
self.check_permissions(request)
self.check_throttles(request)
# 第三步
# APIView類
def check_permissions(self, request):
# 這裡的原理與認證類一致
for permission in self.get_permissions():
# 有一點不同的是has_permission(request, self)中的self是APIView的self
if not permission.has_permission(request, self):
self.permission_denied(request, message=getattr(permission, 'message', None)
)
# self.get_permissions()的來歷
def get_permissions(self):
return [permission() for permission in self.permission_classes]
3.順序
許可權類使用順序:先用檢視類中的許可權類,再用settings裡配置的許可權類,最後用預設的許可權類