본문 바로가기
Django/Django REST framework

14-Authentication

by hyun-am 2021. 9. 26.

인증은 들어오는 request를 request보낸 사용자 또는 서명된 토큰과 같은 일련의 식별 자격 증명과 연결하는 메커니즘입니다. 그런 다음 권한 및 제한 정책은 해당 자격 증명을 사용하여 요청을 허용해야 하는지 여부를 결정할 수 있습니다.

REST 프레임워크는 기본적으로 여러 인증 체계를 제공하며 사용자 지정 체계를 구현할 수도 있습니다.

인증은 항상 view의 맨 처음, 권한 및 throttling 검사가 발생하기 전, 그리고 다른 코드가 계속 진행되도록 허용되기 전에 실행됩니다.

request.user 속성은 일반적으로 contrib.auth 패키지의 User 클래스 인스턴스로 설정됩니다.

request.auth 속성은 추가 인증 정보에 사용됩니다. 예를 들어 request에 서명한 인증 토큰을 나타내는 데 사용할 수 있습니다.

참고 : 인증 자체는 들어오는 요청을 허용하거나 허용하지 않는다는 것을 잊지 마십시오. 단순히 요청에 사용된 자격 증명을 식별합니다.

API에 대한 권한 정책을 설정하는 방법에 대한 정보는 권한 문서를 참조하면 되겠습니다.

링크 : https://www.django-rest-framework.org/api-guide/permissions/

 

Permissions - Django REST framework

 

www.django-rest-framework.org

How authentication is determined

인증 체계는 항상 클래스 목록으로 정의됩니다. REST 프레임워크는 목록의 각 클래스에 대해 인증을 시도하고 성공적으로 인증된 첫 번째 클래스의 반환 값을 사용하여 request.userrequest.auth를 설정합니다.

인증하는 클래스가 없으면 request.user는 django.contrib.auth.models.AnonyumousUser의 인스턴스로 설정되고 request.auth는 없음으로 설정됩니다.

인증되지 않은 요청에 대한 request.user 및 request.auth 값은 UNAUTHENTICATED_USERUNAUTHENTICATED_TOKEN 설정을 사용하여 수정할 수 있습니다.

Setting the authentication scheme

기본 인증 체계는 DEFAULT_AUTHENTICAION_CLASSES 설정을 사용하여 전역적으로 설정할 수 있습니다. 예를들면 아래와 같습니다.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

APIView CBV를 사용하여 view별 또는 viewset으로 인증 체계를 설정할 수도 있습니다.

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ExampleView(APIView):
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        content = {
            'user': str(request.user),  # `django.contrib.auth.User` instance.
            'auth': str(request.auth),  # None
        }
        return Response(content)

또는 함수 기반 보기와 함께 @api_view 데코레이터를 사용하는 경우 다음과 같이 작성할 수 있습니다.

@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    content = {
        'user': str(request.user),  # `django.contrib.auth.User` instance.
        'auth': str(request.auth),  # None
    }
    return Response(content)

Unauthorized and Forbidden responses

인증되지 않은 요청이 권한이 거부되면 적절할 수 있는 두가지 다른 오류 코드가 있습니다.

  • HTTP 401 Unauthorized
  • HTTP 403 Permission Denied

HTTP 401 response에는 클라이언트에게 인증 방법을 지시하는 WWW-Authenticate 헤더가 항상 포함되어야 합니다. HTTP 403 응답에는 WWW-Authenticate 헤더가 포함되지 않습니다.

사용되는 response의 종류는 인증 체계에 따라 다릅니다. 여러 인증 체계를 사용할 수 있지만 Response 유형을 결정하는 데 하나의 체계만 사용할 수 있습니다. view에 설정된 첫 번째 인증 클래스는 response 응답 유형을 결정할 때 사용됩니다.

요청이 성공적으로 인증될 수 있지만 여전히 요청을 수행할 수 있는 권한이 거부되는 경우 인증 체계에 관계없이 항상 403권한 거부 응답이 사용됩니다.

Apache mod_wsgi specific configuration

mod_wsgi를 사용하여 Apache에 배포하는 경우 authorization header는 기본적으로 WSGI 애플리케이션으로 전달되지 않습니다. 인증이 애플리케이션 수준이 아니라 Apache에서 처리된다고 가정하기 때문입니다.

Apache에 배포하고 비세션 기반 인증을 사용하는 경우 필요한 헤더를 애플리케이션에 전달하도록 mod_wsgi를 명시적으로 고성해야 합니다. 이는 적절한 컨텍스트에서 WSGIPassAuthorzation 지시문을 지정하고 'On'으로 설정하여 수행할 수 있습니다.

# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On

API Reference

BasicAuthentication

이 인증 체계는 사용자의 사용자 이름과 암호에 대 서명된 HTTP기본 인증을 사용합니다. 기본 인증은 일반적으로 테스트에만 적합합니다.

성공적으로 인증되면 BasicAuthentication은 다음 자격 증명을 제공합니다.

  • request.user will be a Django User instance.
  • request.auth will be None.

권한이 거부된 인증되지 않은 응답은 적절한 WWW-Authenticate 헤더와 함께 HTTP 401 인증되지 않은 응답을 생성합니다. 예를 들면 다음과 같습니다.

WWW-Authenticate: Basic realm="api"

참고 : 프로덕션에서 BasicAuthentication을 사용하는 경우 API가 https를 통해서만 사용할 수 있는지 확인해야 합니다. 또한 API클라이언트가 로그인할 때 항상 사용자 이름과 비밀번호를 다시 요청하고 이러한 세부 정보를 영구 저장소에 저장하지 않도록 해야합니다.

TokenAuthentication

이 인증 체계는 간단한 토큰 기반 HTTP 인증 체계를 사용합니다. 토큰 인증은 기본 데스크톱 및 모바일 클라이언트와 같은 클라이언트-서버 설정에 적합합니다.

TokenAuthentication 체계를 사용하려면 TokenAuthentication을 포함하도록 인증 클래스를 구성하고 INSTALLED_APPS 설정에 rest_framework.authtoken을 추가로 포함해야 합니다.

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

참고 : 설정을 변경한 후 manage.py migrate를 실행해야 합니다. rest_framework.authtoken 앱은 Django 데이터베이스 마이그레이션을 제공합니다.

사용자를 위한 토큰도 생성해야 합니다.

from rest_framework.authtoken.models import Token

token = Token.objects.create(user=...)
print(token.key)

클라이언트가 인증하려면 Authorization HTTP 헤더에 토큰 키가 포함되어야 합니다. 키는 두 문자열을 구분하는 공백을 사용하여 문자열 리터럴 "토큰"을 접두사로 지정해야 합니다. 예를 들어 :

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

참고 : Bearer와 같이 헤더에서 다른 키워드를 사용하려면 TokenAuthentication을 하위 클래스로 분류하고 키워드 클래스 변수를 설정하면 됩니다.

성공적으로 인증되면 TokenAuthentication은 다음 자격 증명을 제공합니다.

  • request.user will be a Django User instance.
  • request.auth will be a rest_framework.authtoken.models.Token instance.

권한이 거부된 인증되지 않은 응답은 적절한 WWW-Authenticate 헤더와 함께 HTTP 401 인증되지 않은 응답을 생성합니다. 예를들면 다음과 같습니다.

WWW-Authenticate: Token

CLI에서 curl 명령어는 token 인증 API를 테스트하는데 유용할 수 있습니다. 예시 코드는 다음과 같습니다.

curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'

참고 : 프로덕션에서 TokenAuthentication을 사용하는 경우 API를 https를 통해서만 사용할 수 있는지 확인해야 합니다.

Generating Tokens

By using signals

모든 사용자가 자동으로 생성된 토큰을 가지도록 하려면 사용자의 post_save 신호를 잡기만 하면 됩니다.

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)

이 코드 snippet을 설치된 models.py 모듈 또는 시작 시 Django에서 가져올 다른 위치에 배치해야 합니다.

이미 일부 사용자를 생성했다면 다음과 같이 모든 기존 사용자에 대한 토큰을 생성할 수 있습니다.

from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token

for user in User.objects.all():
    Token.objects.get_or_create(user=user)

By exposing an api endpoint

TokenAuthentication을 사용할 때 클라이언트가 사용자 이름과 암호가 지정된 토큰을 얻을 수 있는 메커니즘을 제공할 수 있습니다. REST 프레임워크는 이 동작을 제공하기 위해 기본 제공된 view를 제공합니다. 그것을 사용하려면 URLconf에 get_auth_token view를 추가하면 됩니다.

from rest_framework.authtoken import views
urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)
]

패턴의 URL 부분은 원하는대로 사용할 수 있습니다.

get_auth_token view는 유효한 username과 password 데이터 또는 JSON을 사용하여 view에 게시될 때 JSON응답을 반환합니다.

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }

기본 get_auth_token viewsettings에서 기본 렌더러 및 파서 클래스를 사용하는 대신 JSON request 및 response를 명시적으로 사용합니다.

기본적으로 get_auth_token view에 적용되는 권한 또는 제한이 없습니다. 스로틀에 적용하려면 view class를 재정의하고 throttle_classes속성을 사용하여 포함해야 합니다.

get_auth_token view의 사용자 정의 버전이 필요한 경우에는 GainAuthToken viewclass를 서브클래싱하고 url conf에서 대신 사용하면 됩니다.

예를 들어 토큰 값 이외의 추가 사용자 정보를 반환할 수 있습니다.

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response

class CustomAuthToken(ObtainAuthToken):

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data,
                                           context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        return Response({
            'token': token.key,
            'user_id': user.pk,
            'email': user.email
        })

urls.py에서 다음과 같이 쓸 수있습니다.

urlpatterns += [
    path('api-token-auth/', CustomAuthToken.as_view())
]

With Django admin

관리자 인터페이스를 통해 수동으로 토큰을 생성할 수도 있습니다. 대규모 사용자 기반을 사용하는 경우 TokenAdmin 클래스를 monkey patch하여 필요에 맞게 사용자 커스텀하는 것이 좋습니다. 보다 구체적으로 사용자 필드를 raw_field로 선언하여 사용자 지정하는 것입니다.

your_app/admin.py:

from rest_framework.authtoken.admin import TokenAdmin

TokenAdmin.raw_id_fields = ['user']

Using Django manage.py command

Since version 3.6.4 it's possible to generate a user token using the following command:

./manage.py drf_create_token <username>

이 명령어는 주어진 사용자에 대한 API 토큰을 반환하고 존재하지 않는 경우 생성합니다.

Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1

토큰을 재생성하려는 경우(예:손상되거나 유출된 경우)추가 매개변수를 전달할 수 있습니다.

./manage.py drf_create_token -r <username>

SessionAuthentication

이 인증 체계는 인증을 위해 Django의 기본 세션 백엔드를 사용합니다. 세션 인증은 웹 사이트와 동일한 세션 컨텍스트에서 실행중인 AJAX 클라이언트에 적합합니다.

성공적으로 인증되면 SessionAuthentication은 다음 자격 증명을 제공합니다.

  • request.user will be a Django User instance.
  • request.auth will be None.

권한이 거부된 인증되지 않은 응답은 HTTP403Forbidden response가 됩니다.

SessionAuthentication과 함께 AJAX 스타일 API를 사용하는 경우 PUT, PATCH, POST 또는 DELETE 요청과 같은 "안전하지 않은" HTTP 메서드 호출에 대해 유효한 CSRF 토큰을 포함해야 합니다. 자세한 내용은 Django CSRF 문서를 참고하면 되겠습니다.

주의 : 로그인 페이지를 만들 때는 항상 Django의 표준 login view를 사용해야합니다. 이렇게 하면 login view가 제대로 보호가 됩니다.

REST 프레임워크의 CSRF 유효성 검사는 동일한 view에 대한 세션 및 비세션 기반 인증을 모두 지원해야 하기 때문에 표준 Django와 약간 다르게 작동합니다. 이는 인증된 요청에만 CSRF 토큰이 필요하며 익명 요청은 CSRF 토큰 없이 보낼 수 있음을 의미합니다. 이 동작은 항상 CSRF 유효성 검사가 적용되어야 하는 login view에 적합하지 않습니다.

RemoteUserAuthentication

이 인증 체계를 사용하면 REMOTE_USER 환경 변수를 설정하는 웹 서버에 인증을 위임할 수 있습니다. 이를 사용하려면 AUTHENTICATION_BACKENDS 설정에 django.contrib.auth.backends.RemoteUserBackend(또는 하위 클래스)가 있어야 합니다. 기본적으로 RemoteUserBackend는 이미 존재하지 않는 사용자 이름에 대한 사용자 개체를 만듭니다. 이 동작과 다른 동작을 변경하려면 Django 설명서를 참조하세요.

링크 : https://docs.djangoproject.com/en/stable/howto/auth-remote-user/

성공적으로 인증되면 RemoteUserAuthentication은 다음 자격 증명을 제공합니다.

  • request.user will be a Django User instance.
  • request.auth will be None.

인증 방법 구성에 대한 정보는 웹 서버 설명서를 참조하십시오 예시는 다음과 같습니다.

Custom authentication

사용자 지정 인증 체계를 구현하려면 BaseAuthentication의 하위 클래스를 만들고 .authenticate(self, request)메서드를 재정의 합니다. 메서드는 인증이 성공하면 (user, auth)의 2-튜플을 반환하고 그렇지 않으면 None을 반환해야 합니다.

어떤 상황에서는 None을 반환하는 대신 .authenticate()메서드에서 AuthenticationFailed 예외를 발생시킬 수 있습니다.

일반적으로 취해야 하는 접근 방식은 다음과 같습니다.

  • 인증이 시도되지 않으면 None을 반환합니다. 사용중인 다른 인증 체계도 계속 확인됩니다.
  • 인증을 시도했지만 실패하면 AuthenticationFaield 예외를 발생시킵니다. 권한 검사에 관계없이 다른 인증 체계를 검사하지 않고 오류 응답이 즉시 반환됩니다.

.authenticate_header(self, request) 메서드를 재정의할 수도 있습니다. 구현된 경우 HTTP 401 Unauthorized 응답에서 WWW-Authenticate 헤더 값으로 사용될 문자열을 반환해야 합니다.

.authenticate_header() 메서드가 재정의되지 않으면 인증되지 않은 요청에 대한 액세스가 거부될 때 인증 체계가 HTTP 403 Forbidden 응답을 반환합니다.

참고 : 사용자 지정 인증자가 요청 개체의 .user 또는 .auth 속성에 의해 호출되면 AttributeError가 WrappedAttributeError로 다시 발생하는 것을 볼 수 있습니다. 이는 외부 속성 액세스에 의해 원래 예외가 억제되는 것을 방지하기 위해 필요합니다. Python은 AttributeError가 사용자 정의 인증자에서 발생했다는 것을 인식하지 못하고 대신 요청 객체에 .user 또는 .auth 속성이 없다고 가정합니다. 이러한 오류는 수정하거나 인증자가 처리해야 합니다.

Example

다음 예시에서는 'X-USERNAME'이라는 사용자 지정 요청 헤더에서 사용자 이름으로 제공된 사용자로 들어오는 모든 요청을 인증합니다.

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('HTTP_X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

 

참고 링크 : 

https://www.django-rest-framework.org/api-guide/authentication/

 

Authentication - Django REST framework

 

www.django-rest-framework.org

 

'Django > Django REST framework' 카테고리의 다른 글

Django FCM 개발(DRF)  (0) 2022.03.01
DRF 테스트코드 개발 도전기  (0) 2021.12.17
13-Testing  (0) 2021.08.24
12-Validators  (0) 2021.08.18
11. Serializer relations  (1) 2021.07.18

댓글