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