DRF 에서는 인증 및 권한 관리를 지원합니다.
Authentication 은 유입되는 요청을 허용 / 거부하는 것을 결정하는 것이 아니라, 단순히 인증정보로 유저를 식별하는 것입니다. Authentication 메커니즘을 거친 다음에야 Permission 및 Throtting 정책을 사용하여 요청을 허용하는지에 대한 여부를 결정할 수 있습니다.
- Authentication (인증) : 유저 식별
- Permission (권한) : 각 요청에 대한 허용 / 거부
- Throtting (제한) : 일정 기간 동안에 허용할 최대 요청 횟수
🦝 django-rest-framework/rest_framework/views.py
dispath(request)
호출inital(request)
호출perform_authentication(request)
호출user
속성 호출_authenticate
호출# rest_framework/views.py
# APIView 클래스
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
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
Django 기본에서 User authentication
을 지원해 줬다면, DRF 에서 지원하는 Authentication 은 다음과 같습니다. 인증 구현 시 http 프로토콜을 사용할 경우 암호 유출의 위험이 있으니, https 프로토콜을 사용하여 보안성을 높이도록 합니다.
""" rest_framework/urls.py """
from django.conf.urls import url
from django.contrib.auth import views
app_name = 'rest_framework'
urlpatterns = [
path('login/', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'),
path('logout/', views.LogoutView.as_view(), name='logout'),
]
""" myproject/urls.py """
urlpatterns += [
path('api-auth/', include('rest_framework.urls', namespace='rest_framework)),
]
Authentication 만으로는 개체 (정보/코드 등)에 대한 접근을 허용하는게 충분하지 않습니다. 추가적으로 각 개체의 권한에 대한 Permission 가 필요합니다.
Django 기본 auth 에서 is_superuser
is_staff
is_active
등의 boolean filed 를 지원해 줬다면, DRF 에서 지원하는 Permissions 는 다음과 같습니다.
현재 요청에 대한 허용 / 거부를 결정하며, APIView 단위로 지정할 수 잇습니다.
APIView 에서는 permission_classes
를 통해 권한을 지정할 수 있고
# views.py
""" Django 기본에서는 @login_required 데코레이터나, LoginRequiredMixin 을
사용하요 로그인을 보장받았다면, DRF 에서는 permission_classes 로 지정한다. """
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from .serializers import PostSerializer
from .models import Post
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
@api_view 데코레이터에는 @permission_classes
데코레이터를 설정하면 됩니다.
from rest_framework.decorators import permission_classes
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def post_view(request):
pass
기본 권한 정책을 settings.py
에서 전역적으로 설정할 수 있습니다.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
]
}
모든 Permission 클래스는 다음 2가지 함수를 선택적으로 구현합니다.
has_permission(request, view)
has_object_permission(request, view, obj)
🦝 django-rest-framework/rest_framework/permissions.py
# rest_framework/permissions.py
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
class AllowAny(BasePermission):
"""
Allow any access.
"""
def has_permission(self, request, view):
return True
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
class IsAdminUser(BasePermission):
"""
Allows access only to admin users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
# rest_framework/permissions.py
class DjangoObjectPermissions(DjangoModelPermissions):
...
def has_object_permission(self, request, view, obj):
# authentication checks have already executed via has_permission
queryset = self._queryset(view)
model_cls = queryset.model
user = request.user
perms = self.get_required_object_permissions(request.method, model_cls)
if not user.has_perms(perms, obj):
# If the user does not have permissions we need to determine if
# they have read permissions to see 403, or not, and simply see
# a 404 response.
if request.method in SAFE_METHODS:
# Read permissions already checked and failed, no need
# to make another lookup.
raise Http404
read_perms = self.get_required_object_permissions('GET', model_cls)
if not user.has_perms(read_perms, obj):
raise Http404
# Has read permissions.
return False
return True
# permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
# 인증된 유저에 한해, 목록조회/포스팅등록 허용
def has_permission(self, request, view):
return request.user.is_authenticated
# 작성자에 한해, Record에 대한 수정/삭제 허용
def has_boject_permission(self, request, view, obj):
# 안전한 조회 요청(GET, HEAD, OPTIONS) 에 대해서는 인증여부에 상관없이 허용
if request.method in permissions.SAFE_METHODS:
return True
# PUT, DELETE 요청에 대해, 작성자일 경우에만 요청 허용
return obj.author == request.user
# views.py
...
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
# 인증된 유저에 한해, 목록조회/포스팅등록 허용
def has_permission(self, request, view):
return request.user.is_authenticated
# 작성자에 한해, Record에 대한 수정/삭제 허용
def has_boject_permission(self, request, view, obj):
# 안전한 조회 요청(GET, HEAD, OPTIONS) 에 대해서는 인증여부에 상관없이 허용
if request.method in permissions.SAFE_METHODS:
return True
# DELETE 권한은 superuser 에게만 부여
if(request.method == 'DELETE'):
return request.user.is_superuser
# PUT 요청에 대해, 작성자일 경우에만 허용
return obj.author == request.user