Django框架(二十一)—— Django rest_framework-許可權元件
阿新 • • 發佈:2018-12-13
目錄
Django rest_framework-許可權元件
一、許可權元件的使用
# 使用者資訊表 class UserInfo(models.Model): name = models.CharField(max_length=32) # 寫choice user_choice=((0,'普通使用者'),(1,'會員'),(2,'超級使用者')) # 指定choice,可以快速的通過數字,取出文字 user_type=models.IntegerField(choices=user_choice,default=0) pwd = models.CharField(max_length=32) # 使用者token class UserToken(models.Model): token = models.CharField(max_length=64) user = models.OneToOneField(to=UserInfo)
1、使用語法
from rest_framework.permissions import BasePermission # 寫一個許可權類 class UserPermission(BasePermission): # 重寫沒有許可權時的資料 message = '您沒有許可權' # 重寫has_permission()方法,傳入三個引數 # 第一個是物件自身(自動傳);第二個是request物件;第三個是 def has_permission(self, request, view): # 只要認證通過,就會把當前使用者物件放到request中 user_type = request.user.user_type # get_欄位名_display() 可以獲取choice數字對應的具體值 # user_type_name = request.user.get_user_type_display() # print(user_type_name) if user_type == 2: return True return False
class Book(APIView):
# 使用者認證
authentication_classes = [UserAuth.UserAuth, ]
# 許可權判斷
permission_classes = [MyPerm.UserPermission, ]
def get(self, request, *args, **kwargs):
response = {'status': 100, 'msg': '查詢成功'}
ret = models.Book.objects.all()
ser = MySerializer.BookSerializer(instance=ret, many=True)
response['data'] = ser.data
return JsonResponse(response, safe=False)
2、全域性使用、區域性使用、區域性禁用許可權
(1)全域性使用
- 在settings檔案中配置,配置完以後,就無需在檢視類中寫,已經是所有檢視類都需要許可權判斷
- 必須為
REST_FRAMEWORK
,key值必須為DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK={
'DEFAULT_PERMISSION_CLASSES':['app01.MyPerm.UserPermission',],
}
(2)區域性使用
在需要使用的檢視類中寫,只對當前檢視類起認證作用,重新定義permission_classes
class Book(APIView):
# # 該方法是區域性使用認證
authentication_classes = [UserAuth.UserAuth, ]
# 該方法是區域性使用許可權
permission_classes = [MyPerm.UserPermission, ]
def get(self, request, *args, **kwargs):
response = {'status': 100, 'msg': '查詢成功'}
ret = models.Book.objects.all()
ser = MySerializer.BookSerializer(instance=ret, many=True)
response['data'] = ser.data
return JsonResponse(response, safe=False)
(3)區域性禁用
在配置過全域性許可權判斷以後,有些檢視類不需要判斷許可權,可以區域性禁用許可權證,只需將permission_classes
定義為空列表即可。
class Book(APIView):
# # 該方法是區域性使用認證
authentication_classes = [UserAuth.UserAuth, ]
# 該方法是區域性禁用許可權
permission_classes = []
def get(self, request, *args, **kwargs):
response = {'status': 100, 'msg': '查詢成功'}
ret = models.Book.objects.all()
ser = MySerializer.BookSerializer(instance=ret, many=True)
response['data'] = ser.data
return JsonResponse(response, safe=False)
二、原始碼分析
as_view ----------> view -------------> dispatch -------> Request包裝新的request ------> 認證、許可權、頻率 --------> 根據請求方式分發到不同的方法
url(r'books/',views.Book.as_view())
1、Book中沒有as_view
2、APIView的as_view
class APIView(View):
@classmethod
# cls 是 Book類
def as_view(cls, **initkwargs):
# view = super(APIView, Book).as_view(**initkwargs)
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)
3、view = super(APIView, cls).as_view(**initkwargs) ---------------------> View中的as_view
class View(object):
@classonlymethod
# cls====> Book
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
# 例項化產生一個book物件
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 調dispatch方法
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
4、return self.dispatch(request, *args, **kwargs) ----------------> dispatch
self====> Book物件,一層層找dispatch
APIView中找到dispatch
class APIView(View):
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# (a)初始化request,就是通過Request類來包裝原生request,得到包裝後的request
request = self.initialize_request(request, *args, **kwargs)
# 從現在開始request就是包裝後的request
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# (b) 認證、許可權、頻率
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
# http_method_names表示列表['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
(a)request = self.initialize_request(request, *args, **kwargs) 包裝 request
self 是Book物件
class APIView(View):
# 預設的認證列表類
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# (a-b)例項化初始化產生新的request物件
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 認證類例項化產生的物件的列表
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
(a------1)return Request( ··· ) ----------> Request類初始化
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty
if self.parser_context is None:
self.parser_context = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,)
(b)self.initial(request, *args, **kwargs) -----> 認證、許可權、頻率
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
# (b------1) 認證
self.perform_authentication(request)
# (b------2)許可權
self.check_permissions(request)
# 頻率
self.check_throttles(request)
(b------1) self.check_permissions(request) -------> 許可權判斷
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
# (b------1---------1) get_permissions 許可權類物件組成的列表
for permission in self.get_permissions():
# 重寫的就是這個has_permission()方法,判斷當前使用者是否有許可權
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
(b------1---------1) self.get_permissions() -------> 獲取許可權類物件組成的列表
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]