본문 바로가기
FrameWork/Django

Authentication

by mansoorrr 2024. 7. 25.

Authentication은 인증받는 것을 의미한다.

Django는 세션 베이스로 로그인을 하게되면 세션과 쿠키를 생성한다.

그리고 user가 django페이지에 접속하면 자동으로 쿠키를 읽는다.

 

자동으로 해주지만 커스텀 하기 위해 여러 방법을 알아본다.

 

 

[** django Authentication **]

  • 장고에서 사용하는 default Authentication은 다음과 같다.
  • Authentication은 무조건 views.py들 보다 먼저 실행된다.
#---------- config/settings.py

<생략...>
#Authentication
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
    ]
}

 

 

 

[** Custom Authentication **]

  • 나만의 Authentication Class를 만들어 settings에 적용 가능
    • user만 리턴하면 됨. 그러면 view에서 사용하는 reques.user에 사용 가능
    • rest_framwork.authentication.BaseAuthentication을 상속받아야 함
      • authenticate를 override해야함
      • 인자로 self, request를 받음 -> request에는 user에 해당하는 값이 없음
      • user를 찾아 반환하거나 없으면 None을 반환
'''
[Authetication 만들기]
1. 클래스 만들기
2. authenticate override
3. settings에 적용

'''
#---------- config/authentication.py

class TrustMeBroAuthentication(BaseAuthentication):

    def authenticate(self, request):
        print(request.headers)
        return None #None을 반환했으므로 인증이 되지 않을 것임
        
        
#---------- config/settings.py
#Authentication
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'config.authentication.TrustMeBroAuthentication',
    ]
}

custom authentication에 return을 None으로 설정한 결과: 인증 못함

  • user를 반환할 수 있도록 코드 작성
class TrustMeBroAuthentication(BaseAuthentication):

    def authenticate(self, request):
        username = request.headers.get('Trust-me')
        if not username:
            return None
        try:
            user = User.objects.get(username=username) #user찾기
            return (user, None) #user반환
        except User.DoesNotExist:
            raise AuthenticationFailed(f"not user: {username}")

 

 

 

[** Token Authentication **]

  • django는 기본적으로 TokenAuthentication이 내장되어 있음
  • TokenAuthentication이 적용되는 방식
    • django가 사용자에게  토큰을 부여 (id와 password를 통해)
    • 사용자는 부여받은 토큰을 통해 django에 접속
    • django는 사용자별로 부여한 토큰과 사용자가 접속할때 입력한 토큰을 비교하여 일치할 경우 데이터 반환
  • 사용방법 및 순서
    • settings.py에 rest_framework.authtoken 추가(INSTALLED_APPS)
    • settings.py에 REST_FRAMEWORK에 rest_framework.authentication.TokenAuthentication추가
    • python manage.py migrate: admin패널에 토큰 테이블 생성
    • 토큰을 얻기 위한 url 추가
      • postman을 활용해 해당 url로 post요청 보내면 토큰이 생성됨(body에 id, password입력)
    • 부여받은 토큰으로 데이터 확인
      • postman을 통해 /api/v1/users/me로 get방식 요청
      • header에 부여받은 인증 토큰을 담아서 보내야 함
        • key: Authorization
        • value: Token <부여받은 토큰>
        • value 작성시 Token입력 후 띄어쓰기 꼭 해야함
#-------------------- config/settings.py

# Application definition
THIRD_PARTY_APPS = [
    "rest_framework",
    "rest_framework.authtoken",
]

# Application definition
CUSTOM_APPS = [    
    'users.apps.UsersConfig',
    'rooms.apps.RoomsConfig',
    'common.apps.CommonConfig',
    'experiences.apps.ExperiencesConfig',
    'categories.apps.CategoriesConfig',
    'reviews.apps.ReviewsConfig',
    'wishlists.apps.WishlistsConfig',
    'bookings.apps.BookingsConfig',
    'medias.apps.MediasConfig',
	'direct_messages.apps.DirectMessagesConfig',
]

SYSTEM_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

INSTALLED_APPS = SYSTEM_APPS + THIRD_PARTY_APPS + CUSTOM_APPS

#Authentication
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'config.authentication.TrustMeBroAuthentication',
        'rest_framework.authentication.TokenAuthentication', #토큰 인증
    ]
}
#----------- users/urls.py
from rest_framework.authtoken.views import obtain_auth_token #토큰 받기 위한 rest_framework의 패키지

urlpatterns = [
    path('', Users.as_view()),
    path('me/', Me.as_view()),
    path('<str:username>', PublicUser.as_view()),
    path('change-password/', ChangePassword.as_view()),
    path('log-in/', LogIn.as_view()),
    path('log-out/', LogOut.as_view()),
    path('token-login/', obtain_auth_token), #토큰을 받기 위한 url 추가
]

 

 

[** JWT Authentication **]

  • Json Web token
  • db에 공간을 잡아먹지 않음
  • 암호화된 정보를 토큰으로 만들어 유저에게 부여
  • 유저가 부여받은 토큰을 django에게 주면 토큰을 해독하여 데이터 보여줌
  • pyJWT 라이브러리 사용
    • encode: 사용자 정보를 토큰화하여 전달
      • payload: user의 어떤 정보를 token화 할 것인지 정함(중요한 정보로 만들면 안됨)
      • key: django settings.py에 있는 secret key
      • algorithm: 토큰 변환에 사용되는 알고리즘
    • decode: 토큰을 사용자 정보로 변환
  • 별도의 라이브러리는 없으므로 직접 토큰만들어 로그인 하는 로직 구현(Encode)
##---------------- ENCODE

#---------- users/urls.py
urlpatterns = [
    path('', Users.as_view()),
    path('me/', Me.as_view()),
    path('<str:username>', PublicUser.as_view()),
    path('change-password/', ChangePassword.as_view()),
    path('log-in/', LogIn.as_view()),
    path('log-out/', LogOut.as_view()),
    path('token-login/', obtain_auth_token),
    path('jwt-login/', JwtLogin.as_view()), #jwt-login
]

#---------- users/views.py
import jwt
from django.conf import settings

class JwtLogin(APIView):

	def post(self, request):
    	username = request.data.get('username')
        password = request.data.get('password')
        if not username or not password:
        	raise ParseError('입력하세요')
            
        user = authenticate(
        	request=request,
            username=username,
            password=password
        )
        
        if not user:
        	raise ParseError('그런사람 없어요')        
        #토큰 부여를 위한 encode
        token = jwt.encode(
        	payload={'pk':user.pk},
            key=settings.SECRET_KEY,
            algorithm='HS256'
        )
        return Response({'token':token})

 

 

  • Decode
    • 별도의 authentication class 만들어 복호화 실시
    • config/settings.py에 등록
#---------- config/authentication.py
class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.headers.get('Jwt')
        if not token: #토큰 없으면 유저정보 없음
            raise None
        #복호화
        decoded = jwt.decode(
            token,
            key=settings.SECRET_KEY,
            algorithms=['HS256'],
        )
        pk = decoded.get('pk') #encoding시에 user의 어떤 정보를 토큰으로 만들것인가(pk)
        if not pk:
            raise AuthenticationFailed('Invalid Token')
        try:
            user = User.objects.get(pk=pk)
            return (user, None)
        except:
            raise AuthenticationFailed('user not found')
        
        
#---------- config/settings.py
#Authentication
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'config.authentication.TrustMeBroAuthentication',
        'rest_framework.authentication.TokenAuthentication',
        'config.authentication.JWTAuthentication',
    ]
}

'FrameWork > Django' 카테고리의 다른 글

Test  (0) 2024.07.26
Permission Classes  (0) 2024.07.22
File Upload  (0) 2024.07.21
Pagination  (0) 2024.07.21
DB transaction  (0) 2024.07.20