Backend/Django

[django-rest-framework] (11) Authentication / Permission

dltjdn 2022. 2. 7. 16:55

 

Authentication / Permission / Throttiling 구분

1. Authentication(인증) : 유저 식별

2. Permission(허가) : 유저 식별 후, 해당 유저의 요청에 대한 허용/거부 결정

3. Throttling : 허용된 유저에 대해,  일정 기간 동안에 허용할 최대 요청 횟수

 

 

인증(Authentication) 처리 순서

1. 매 요청 시마다 APIView의 dispatch(requet) 호출

2. APIView의 initial(request) 호출

3. APIView의 perform_authentication(request) 호출

4. request의 user속성 호출( rest_framework.request.Request 타입 )

5. request의 _authenticate() 호출 → 실제 인증 여부 체크, 인증에 실패하면 not_authenticated() 멤버함수

 

지원하는 인증 (Authentication) 종류

SessionAuthentication

  • 일반 웹에서 사용
  • 한번만 로그인하면 정보가 세션에 저장되어 있음
  • APIView에서 디폴트 지정

BasicAuthentication

  • 매 요청마다 요청 헤더에다 Basic 인증 정보를 담아 인증 (ex. Authorization: Basic YWxsaWV1cxE6MTAoXYWtl)     → Basic 뒤에 부분은 유저명:암호라 요청을 보낼때 보안을 위해 반드시 https 프로토콜 사용해야함  
  • HTTPie를 통한 요청 : http --auth 유저명:암호 --form POST :8000 필드명1:값1 필드명2:값2

TokenAuthentication

  • 매 요청마다 요청 헤더에다 Token 정보를 담아 인증 ( ex. Authorization: Token 401f54qkadfijaojkgjaljfdjdflkjfe)
  • 랜덤을 통해서 각 유저마다 유니크한 토큰을 할당받으며, 토큰만 있으면 인증됨
  • 토큰에 대한 유효성 만료가 없고 유저별로 하나만 할당되기에 유출되면 위험하므로 요즘에는 JWT를 주로 사용

 

웹브라우저를 통한 API 접근에서 세션인증 지원

장고 기본 앱 django.contrib.auth를 통한 SessionAuthentication지원

from django.conf.urls import url
from django.contrib.auth import views

app_name = 'rest_framework'
urlpatterns = [
	url(r'^login/$', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login')
    url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
]

urls.py에 다음과 같은 코드를 추가해 사용 가능

#urls.py

urlpatterns += [
	path('api-auth/', include('rest_framework.urls', namespace='rest_framwork')
 ]

 

 

DRF의 허가(Permission) 

현재 요청에 대한 허용/거부를 결정하며 APIView 단위로 지정 가능하다

 

DRF의 permission

  • AllowAny ( 디폴트 전역 설정 ) : 인증 여부에 상관없이, 뷰 호출 허용
  • IsAuthenticated : 인증된 요청에 한해서, 뷰 호출 허용
  • IsAdminUser : staff인증 요청에 한해서, 뷰 호출 허용
  • IsAuthenticatedOrReadOnly : 비인증 요청에게는 읽기/조회만 허용
class AllowAny(BasePermission):
	def has_permission(self, request, view):
    		return True
     
class IsAuthenticated(BasePermission):
	def has_permission(self, request, view):
    		return request.user and request.user.is_authenticated
     
class IsAdminUser(BasePermission):
	def has_permissino(self, request, view):
    		return request.user and request.user.is_staff

 

django 기본 permission

  • DjangoModelpermissions : 인증된 요청에 한해 뷰 호출을 허용하고, 추가로 장고의 모델단위 Permissions체크  
  • DjangoModelpermissionsOrAnonReadOnly :DjangoModelpermissions와 유사, 비인증 요청에게는 읽기/조회만 허용
  • DjangoObjectPermissions : 비인증 요청은 거부하고, 인증된 요청은 object에 대한 권한 체크를 수행

 

 

Permission 사용 방법 

1. 클래스 기반 뷰

# views.py

from rest_framework.permissions import IsAuthenticated

class ExampleView(APIView):
    permission_classses = [IsAuthenticated]

    def get(self, request, format=None):
        content = { 'status' : 'request was permitted '}
        return Response(content)

2. 함수 기반 뷰

# views.py

from rest_framework.decorators import permission_classes

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    content = { 'status' : 'request was permitted '}
    return Response(content)

3. 디폴트 전역 지정

# settings.py

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

 

 

커스텀 Permission

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 반환

 

Ex 1  포스팅 작성자에게만 수정, 삭제 권한 부여

# permissions.py

from rest_framework import permissions

class IsAuthorOrReadonly(permissions.BasePermission):
    # 인증된 유저에 한해, 목록조회/포스팅등록 허용
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated
    
    # 작성자에 한해, record에 대한 수정/삭제 허용
    def has_obejct_permissions(self, request, view, obj):
        # 조회 요청(GET, HEAD, OPTIONS)에 대해서는 인증여부에 상관없이 허용
        if request.method in permissions.SAFE_METHODS:
            return True
        
        # PUT, DELETE 요청에 대해서는 작성자일 경우에만 허용
        return obj.author == request.user  #author 필드가 있다고 가정

 

Ex 2 포스팅 작성자에게는 수정권한 부여, superuser에게만 삭제 권한 부여

# permissions.py

from rest_framework import permissions

class IsAuthorUpdateOrReadonly(permissions.BasePermission):
 
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated
    
    def has_obejct_permissions(self, request, view, obj):
      
        if request.method in permissions.SAFE_METHODS:
            return True

        if (request.method == 'DELETE'):
            return request.user.is_superuser #또는 request.user.is_staff
        
        return obj.author == request.user