TIL(22/11/25)

김규현·2022년 11월 28일
0

💻 Today I Learn

지난번 API의 접근 권한을 permissions.IsAuthenticated로 설정하면서 인증된 사용자에 한해서 API에 접근할 수 있도록 설정해주었는데 접근 권한과 사용자 인증을 하는 방법이 어떤 것들이 있을지 궁금해졌다.

📌 Authentication과 Permission 이란?

👉 유입되는 요청을 허용 또는 거부하는 것이 아닌, 단순히 사용자를 식별하는 것을 인증(Authentication) 이라 한다.

👉 인증된 사용자가 어떤한 요청을 허용 또는 거부하는 것은 허가(Permission)이다.

예를 들어, 서비스에 가입된 회원만 확인할 수 있는 페이지는 로그인이 필요하고, 다른 사람의 글을 삭제 및 수정을 하기 위한 권한 등에 있어서는 허가가 필요하다.

📌 Authentication

authentication(인증)은 특정 서비스를 사용하는 데 있어 사용자의 신원(회원/비회원/관리자 등을 확인)을 확인하는 절차를 의미한다.

지원하는 인증의 종류는 총 5가지가 있다.

  • SessionAuthentication(세션을 통한 인증 여부 체크)
    로그인될 때마다 저장되는 session 정보를 통해 인증

  • BasicAuthentication(Basic 인증헤더를 통한 인증 수행)
    HTTP 제어 header로 넘긴 id와 password를 base64로 encoding (보안 상의 위협이 있을 수 있음, 테스트에 적절)
    ex) Authorization: Basic YWxsaWV1czE6MTAyOXNoYWtl

  • TokenAuthentication(Token 헤더를 통한 인증 수행)
    token으로 인증, 인증 요청을 보낼 시 key 값을 되돌려주는 방식 (client-server 관계에서 사용하기에 적절)
    ex) Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a

  • RemoteUserAuthentication
    User 정보가 다른 서비스에서 관리될 때, Remote 인증 (장고 공식문서)
    Remote-User 헤더를 통한 인증 수행

  • Custom Authentication
    개발자가 custom하여 authentication을 만들어서 사용할 수도 있다.

만약 모든 view에서 동일한 authentication을 사용하고 싶다면 아래와 같이 DEFAULT_AUTHENTICATION_CLASSES 추가해주면 원하는 인증 방식을 모든 View에서 default로 적용이 된다.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
	    'rest_framework_simplejwt.authentication.JWTAuthentication', # jwt 토큰 인증 방식
        'rest_framework.authentication.BasicAuthentication', # Basic 인증헤더를 통한 인증
        'rest_framework.authentication.SessionAuthentication', # 세션을 통한 인증 방식
    ]
} 

📌 Permission

permission(권한)은 특정 서비스를 어느 정도로 이용할 수 있는지에 대한 권한을 의미한다.

django에서 제공하는 기본적인 권한

  • is_superuser : True일 경우 createsuper 로 생성한 user는 별도 permission 없이 모든 권한 허용
  • is_staff : True일 경우 admin 페이지 접속이 가능하며 그 외 일반 유저와 동일

  • is_active : 계정의 활성 상태를 나타내며 False 일 경우 모든 권한 불허하며 로그인도 되지 않는다.

DRF 에서 제공하는 기본 Permission

  • AllowAny: 인증/비인증 모두 허용 (default)

  • IsAuthenticated: 인증된 요청에 대해서만 view 호출

  • IsAdminUser: Staff User에 대해서만 요청 허용 (User.is_staff가 True여야 함)

  • IsAuthenticatedOrReadOnly: 비인증 요청에 대해서는 읽기만 허용

  • DjangoModelPermissions: 사용자 인증과 관련 모델 권한이 할당된 경우 허용 (django.contrib.auth 모델 permission과 관련 있음)

  • DjangoModelPermissionOrAnonReadonly: DjangoModelPermission과 유사, 비인증 요청에 대해서는 읽기만 허용

  • DjangoObjectPermissions: 모델에 대한 객체 별로 권한이 할당된 경우 허용

  • Custom Permission: 개발자가 custom 하게 permission을 만들어서 사용할 수도 있음

모든 View에 대해 동일한 permission을 적용하고 싶을 때는 settings.py에서 아래와 같이 DEFAULT_PERMISSION_CLASSES에 추가해주면 모든 View는 설정한 권한에 따라 접근할 수 있다.

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
    	# 모든 권한을 허가
        'rest_framework.permissions.AllowAny',
        # 인증된 사용자에 대해서는 모든 권한을 허가하고, 인증되지 않은 사용자는 읽기 권한만 허용
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
		# 인증된 사용자에 대해서만 모든 권한을 허가        
        'rest_framework.permissions.IsAuthenticated',
    ]
}

📌 권한 지정하는 방법

APIView 에서는 permission_classes 을 통해 권한을 지정할 수 있다.
아래와 같이 리스트 형식으로 permission을 설정해준다.

class ProfileView(APIView):
	# 인증/비인증 모두에게 제어할 수 있는 권한을 허용 (default) 
    permission_classes = [permissions.AllowAny]
    # 로그인 인증이 된 유저에게만 제어할 수 있는 권한을 허용
    permission_classes = [permissions.IsAuthenticated]
    # 로그인 인증이 된 유저는 view를 제어할 수 있는 권한을 주고, 인증되지 않은 유저는 읽기 권한만 부여 
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

아래는 프로필 조회/수정을 하는 View이며 서비스는 로그인 후 이용할 수 있다는 가정하에 로직을 짜봤다.

우선 get 메소드의 permission은 IsAuthenticated로 설정했기 때문에 로그인 된 사용자만 접근할 수 있고, put 메소드는 해당 프로필이 본인일 경우에만 수정이 가능하도록 IsOwnerOrReadOnly라는 이름으로 permission을 커스텀하여 적용해주었다.

permission을 커스텀하기 위해 permissions.py를 생성하여 아래와 같이 코드를 작성해주면 해당 권한은 SAFE_METHOD(GET, HEAD, OPTIONS)로 요청이 들어온 경우에는 method를 허용을 해주고, 그 외 (PUT)에는 게시글의 user와 로그인된 user가 동일한 경우에만 권한을 허용해준다.

# permissions.py
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        # 읽기 권한 요청이 들어오면 허용
        if request.method in permissions.SAFE_METHODS:
            return True
        
        # 요청자(request.user)가 객체(Profile)의 user와 동일한지 확인
        return obj.user == request.user

permissions.py에서 커스텀한 IsOwnerOrReadOnly를 import 해준 다음 pul 메소드에 적용하여 해당 프로필이 본인일 경우에만 수정이 가능하도록 권한을 설정했다.

from rest_framework.views import APIView
from users.serializers import ProfileSerializer, ProfileUpdateSerializer
from rest_framework import permissions
from rest_framework.permissions import IsAuthenticated
from .permissions import IsOwnerOrReadOnly

# views.py - 프로필 조회/수정
class ProfileView(APIView):
    def get(self, request):
    	permission_classes = [permissions.IsAuthenticated]
        profile = User.objects.get(id=request.user.id)
        serializer = ProfileSerializer(profile)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def put(self, request):
    	permission_classes = [IsOwnerOrReadOnly]
        profile = User.objects.get(id=request.user.id)
        serializer = ProfileUpdateSerializer(profile, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

참고한 블로그 링크

profile
웹개발 회고록

0개의 댓글