(DRF) Authentication 과 Permisson

duo2208·2022년 2월 3일
0

Django

목록 보기
18/23
post-thumbnail

Authentication : 인증


🚀 (DRF API Guide) Authentication

DRF 에서는 인증 및 권한 관리를 지원합니다.

Authentication 은 유입되는 요청을 허용 / 거부하는 것을 결정하는 것이 아니라, 단순히 인증정보로 유저를 식별하는 것입니다. Authentication 메커니즘을 거친 다음에야 Permission 및 Throtting 정책을 사용하여 요청을 허용하는지에 대한 여부를 결정할 수 있습니다.

  • Authentication (인증) : 유저 식별
  • Permission (권한) : 각 요청에 대한 허용 / 거부
  • Throtting (제한) : 일정 기간 동안에 허용할 최대 요청 횟수

Authentication 처리 순서

🦝 django-rest-framework/rest_framework/views.py

  1. 매 요청 시마다 APIView 의 dispath(request) 호출
  2. APIView 의 inital(request) 호출
  3. APIView 의 perform_authentication(request) 호출
    ›› 세션 인증에서는 로그인을 할 때만 인증을 한 번 하고 마치지만,
    ›› API 인증에서는 클라이언트가 서버에 요청을 할때마다 매번 인증을 시행한다.
  4. request 의 user 속성 호출
  5. request의 _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)

Authentication 종류

Django 기본에서 User authentication 을 지원해 줬다면, DRF 에서 지원하는 Authentication 은 다음과 같습니다. 인증 구현 시 http 프로토콜을 사용할 경우 암호 유출의 위험이 있으니, https 프로토콜을 사용하여 보안성을 높이도록 합니다.

  • SessionAuthentication
    • 세션을 통한 인증.
    • APIView 에서 default 지정.
  • BasicAuthentication
    • Basic 인증 헤더를 통한 인증.
    • APIView 에서 default 지정.
    • ex) Authorization : Basic YWxsaWV1czE6MTAyOxNoYWtl
  • TokenAuthentication
    • Token 헤더를 통한 인증.
    • ex) Authorization : Token 401f7ac837da42b8sdfjeioqpokdwpq11
    • DRF 의 Token 인증 보다는 JWT(Json Web Token) 을 많이 이용.
  • RemoteUserAuthentication
    • User 정보가 다른 서비스에서 관리될 때, Remote 인증.
    • Remote-User 헤더를 통한 인증 수행.



+ 예시) 웹브라우저를 통한 API 접근에서의 로그인 / 로그아웃 지원

""" 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)),
]



Permission : 권한


🚀 (DRF API Guide) permissions

Authentication 만으로는 개체 (정보/코드 등)에 대한 접근을 허용하는게 충분하지 않습니다. 추가적으로 각 개체의 권한에 대한 Permission 가 필요합니다.

Permission 종류

Django 기본 auth 에서 is_superuser is_staff is_active 등의 boolean filed 를 지원해 줬다면, DRF 에서 지원하는 Permissions 는 다음과 같습니다.

현재 요청에 대한 허용 / 거부를 결정하며, APIView 단위로 지정할 수 잇습니다.

  • AllowAny
    • 인증 여부에 상관없이, 뷰 호출 허용
  • IsAuthenticated
    • 인증된 요청에 한해서, 뷰 호출 허용
  • IsAdminUser
    • Staff 인증 요청에 한해서, 뷰 호출 허용
  • IsAuthenticatedOrReadOnly
    • 비인증 요청에게는 읽기 권한만 허용
  • DjangoModelPermissions
    • 인증된 요청에 한해 뷰 호출을 허용하고, 추가로 장고의 모델단위 Permissions 체크
  • DjangoModelPermissionsOrAnonReadOnly
    • DjangoModelPermissions 와 유사하나, 비인증 요청에게는 읽기만 허용
  • DjangoObjcetPermissions
    • 비인증 요청은 거부하고, 인증된 요청은 Object 에 대한 권한 체크를 수행

+ permission_classes 지정

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

+ default 전역 설정

기본 권한 정책을 settings.py 에서 전역적으로 설정할 수 있습니다.

# settings.py

REST_FRAMEWORK = {
	'DEFAULT_PERMISSION_CLASSES': [
    	'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ]
 }



커스텀 Permission

모든 Permission 클래스는 다음 2가지 함수를 선택적으로 구현합니다.

  • has_permission(request, view)
    • APIView 접근 시 체크
    • 거의 모든 Permission 클래스에서 구현하며, 로직에 따라 True / False 반환
  • has_object_permission(request, view, obj)
    • APIView 의 get_object 함수를 통해 object 획득 시에 체크
    • 브라우저를 통한 API 접근에서 CREATE / UPDATE Form 노출 시에 체크
    • DjangoObjectPermissions 에서 구현하며, 로직에 따라 True / False 반환

🦝 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]

+ 예시) 포스팅 작성자에겐 수정 권한을 부여하되 삭제 권한은 superuser 에게만 허용

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

📌 참고 출처

0개의 댓글