📘 DRF 


들어가기에 앞서,

해주세요 😃

  • 데이터베이스를 초기화하고 비어있는 Custom User Model을 만들어 주세요.
    • accounts 앱을 생성하고 User Model을 작성해 주세요.
    • settings에 AUTH_USER_MODEL도 설정해야 합니다.
    • /api/v1/accounts/로 들어오면 accounts 앱의 urls로 연결해 주세요.
    • superuser를 하나 생성해 주세요.
  • DB를 초기화했으니 seeding을 이용해서 article과 comment 데이터를 채워주세요.

 

↓ 해보자

1. 데이터 베이스 초기화

더보기
변경 전

 

이렇게 삭제했어요

 

변경 후

2. accounts app 생성

더보기
python manage.py startapp accounts
settings.py에 앱 등록하기

3. Custom User Model 생성

더보기
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

4. settings.py에 AUTH_USER_MODEL 설정

더보기
# Custom User Model
AUTH_USER_MODEL = "accounts.User"

 

migrations랑 migrate 하기
python manage.py makemigrations
python manage.py migrate

5. api_pjt/urls.py

더보기
path('api/v1/accounts/', include('accounts.urls')),

 

6. superuser 생성

더보기
python manage.py createsuperuser

7. seeding 생성

더보기
python manage.py seed articles --number=30

 

특정 모델에 20개 생성하기
python manage.py seed articles --number=50 --seeder "Comments.article_id" 1

JSON Web Token, JWT

🍪 Session & Cookie

⭐ 쿠키 (Cookie)
  • 웹 브라우저와 요청과 응답을 주고받을 때 사용하는 데이터 조각
  • 쿠키는 도메인에 제한적이며 유효기간이 정해져 있음
  • Auth 외에도 다양한 방식으로 활용
⭐ 세션 (Session) == database
  • stateless 한 HTTP 특징을 보완하기 위한 방법
  • 세션 DB를 이용해서 유저의 정보를 기억하며 Session ID라고 하는 랜덤 한 Key를 쿠키에 담아서 Auth에 활용
  • 쿠키를 사용해서 Session ID를 주고받는 것

 

JSON Web Token, JWT

간단 개요

  • Cookie는 웹 브라우저에만 존재하는 것으로 다양한 장치들과 공통적으로 사용할 수 있는 방식이 필요하다.
    • 이러한 방식으로 널리 사용되는 방법 중 하나가 바로 Token Auth, 그중에서도 JWT 방식이 많이 사용됩니다.
  • Token
    • 간단히 말해 랜덤 하게 생긴 문자열이며,
    • 일정한 규칙을 가지고 있고
    • 간단한 서명을 더한 문자열로 토큰 자체에 유저에 대한 간단한 정보가 들어있는 형태이다.
  • JWT 방식으로 Auth를 처리하면 Session DB나 인증을 위한 여러 가지 로직 처리가 필요 없어진다.

처리방식

  1. 클라이언트가 ID/PW를 서버로 보낸다.
  2. 서버에서 ID/PW를 검증하고 유효하다면 일정한 형식으로 서명 처리된 Token을 응답한다.
  3. 이후 클라이언트는 모든 요청 헤더에 토큰을 담아 서버로 요청을 전송한다.
  4. 서버는 해당 토큰의 유효성을 검증하고 유저의 신원과 권한을 확인 후 요청을 처리한다.

세션과의 차이

  • 세션 데이터베이스가 존재하지 않으며 데이터베이스가 필요하지 않다.
  • 토큰 자체가 하나의 인증 데이터이다.
  • 서버는 '토큰이 유효한가'만 검증하여 처리한다.

장점과 단점

👍🏻 장점

  • 서버에서 관리하는 데이터가 없으므로 복잡한 처리 로직이 필요하지 않다.
  • 세션이나 DB 없이 유저를 인증하는 것이 가능하다.

👎🏻 단점

  • 일방적으로 로그인을 무효화하는 등의 처리가 불가능하다. (세션 테이블 X)
    • 모든 기기 로그아웃
    • 현재 접속 유저 관리 등
  • Token 자체가 데이터를 담고 있는 정보이므로 탈취당할 시 보안이 취약하다.

JWT 구조

  • 🔗 https://jwt.io/
  • eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    • . 을 기준으로 HEADER . PAYLOAD . VERIFY SIGNATURE 세 부분으로 구성된다.
  • HEADER (알고리즘)
    • 토큰의 타입(jwt) 또는 서명 부분의 생성에 어떤 알고리즘(alg)이 사용되었는지 등을 저장
  • PAYLOAD (데이터가 들어가는 부분)
    • 토근 발급자, 토큰 대상자, 토큰 만료시간, 활성날짜, 발급시간 등등
    • 여러 데이터가 담겨있는 부분
    • 이곳에 서비스가 유저 정보를 담아서 인증
    • 누구나 풀어볼 수 있기 때문에 민감한 정보를 담지 않고 최소의 정보만 저장
      • user의 pk 값 등
    • 각각의 데이터는 Claim이라고도 하며 Key-Value 형태로 구성됨
  • SIGNATURE (서명)
    • HEADER + PAYLOAD + 서버의 비밀키 값을 HEADER에 명시된 암호 알고리즘 방식으로 생성한 값
      • PAYLOAD의 글자 하나만 달라져도 SIGNATURE는 완전히 다른 문자열로 변환되어 서버의 비밀키 값을 모른다면 유효한 서명값을 만들어내는 것이 불가능
    • 서버는 토큰을 받으면 'HEADER (+) PAYLOAD (+) 비밀키'로 생성한 서명값이 토큰의 서명값과 일치하는지를 확인하는 과정을 거쳐서 유효성 여부를 확인
    • 서명의 유효여부 + 유효기간 내의 토큰인지 확인하여 Auth 과정을 처리

Access Token과 Refresh Token

  • JWT 인증 방식은 굉장히 장점이 많은데 단점도 확실하다고 할 수 있다.
    • 토큰의 유효시간을 매우 짧게 잡아버리자!
    • 어떻게 보완할 수 있을까? 🤔

➡️ Access Token

  • 요청할 때 인증을 위해 헤더에 포함해야 하는 토큰이다.
  • 매 요청 시 보내는 토큰이므로 보안이 취약하다.
  • 만료 기한을 짧게 잡아 만약 탈취 당해도 짧은 시간 안에 유효하지 않은 토큰 형태가 된다.

🔃 Refresh Token

  • Access Token이 만료되었을 때 새로 Access Token을 발급받기 위한 Token이다.
  • Access Token 보다 긴 유효기간을 가진다.
  • 주로 사용자의 기기에 저장해 두고 사용되며 만약 Refresh Token까지 만료되었다면 다시 인증(로그인) 과정이 필요하다.
  • Refresh Token의 탈취를 보완하기 위해 DB 리소스를 사용하는 다양한 방식도 존재한다. (BlackList 등)

JWT 구현하기

🔗 공식 문서

 

↓ 해보자

1. pip install simplejwt

더보기
공식 문서
pip install djangorestframework-simplejwt

설치해주셨으면 freeze도 해주세요!

pip freeze > requirements.txt

2. settings.py

더보기
공식 문서
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
}

3. accounts/urls.py

더보기
공식 문서
from django.urls import path
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path("signin/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
    path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]

 

공식 문서에 있어요!

 

api/token 대신에 signin/ 으로,

api/token/refresh/ 대신에 token/refresh/로 해주었어요

4. Login JSON (Postman)

더보기
공식 문서

 

공식 문서에서 키와 값이 뭔지 알 수 있어요.

5. Refresh Token (Postman)

더보기
공식 문서

 

JWT Settings

🔗 공식 문서

 

JWT 유효기간 설정

1. api_pjt/settings.py

더보기
공식 문서
from datetime import timedelta

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    "ROTATE_REFRESH_TOKENS": False,
    "BLACKLIST_AFTER_ROTATION": False,
    "UPDATE_LAST_LOGIN": False,
}

 

ACCESS_TOKEN_LIFETIME

A datetime.timedelta object which specifies how long access tokens are valid. This timedelta value is added to the current UTC time during token generation to obtain the token’s default “exp” claim value.

 

REFRESH_TOKEN_LIFETIME

A datetime.timedelta object which specifies how long refresh tokens are valid. This timedelta value is added to the current UTC time during token generation to obtain the token’s default “exp” claim value.

 

ROTATE_REFRESH_TOKENS

When set to True, if a refresh token is submitted to the TokenRefreshView, a new refresh token will be returned along with the new access token. This new refresh token will be supplied via a “refresh” key in the JSON response. New refresh tokens will have a renewed expiration time which is determined by adding the timedelta in the REFRESH_TOKEN_LIFETIME setting to the current time when the request is made. If the blacklist app is in use and the BLACKLIST_AFTER_ROTATION setting is set to True, refresh tokens submitted to the refresh view will be added to the blacklist.

 

BLACKLIST_AFTER_ROTATION

When set to True, causes refresh tokens submitted to the TokenRefreshView to be added to the blacklist if the blacklist app is in use and the ROTATE_REFRESH_TOKENS setting is set to True. You need to add 'rest_framework_simplejwt.token_blacklist', to your INSTALLED_APPS in the settings file to use this setting.

Learn more about Blacklist app.

 

UPDATE_LAST_LOGIN

When set to True, last_login field in the auth_user table is updated upon login (TokenObtainPairView).

Warning: Updating last_login will dramatically increase the number of database transactions. People abusing the views could slow the server and this could be a security vulnerability. If you really want this, throttle the endpoint with DRF at the very least.

 

 

2. Blacklist app

더보기
BLACKLIST_AFTER_ROTATION

When set to True, causes refresh tokens submitted to the TokenRefreshView to be added to the blacklist if the blacklist app is in use and the ROTATE_REFRESH_TOKENS setting is set to True.
You need to add 'rest_framework_simplejwt.token_blacklist', to your INSTALLED_APPS in the settings file to use this setting.

Learn more about 🔗Blacklist app.

 

True로 바꿔준 후에 migrate를 해줘야 한대요

 

"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,

 

migrate
python manage.py migrate

 

 

그럼 이렇게 SQLite가 생겨요!

postman에 가서 잘 되는지 확인해 볼게요!

 

 

이전에 생성해 뒀던 refresh 값을 다시 Send 해볼게요

 

 

됐어요!!


접근 제한하기

로그인 또는 회원가입을 한 뒤 서비스를 이용할 수 있게 제한하기

 

유저가 아니면 Article 기능 접근 제한

articles/views.py

더보기
from rest_framework.permissions import IsAuthenticated

# 글 목록 보기, 글 작성하기
class ArticleListAPIView(APIView):  # GET, POST만 정의돼 있어서 그 외의 method엔 작동 X
    
    #APIView에 있는 permission_classes를 활용해서 접근 제한하기
    permission_classes = [IsAuthenticated]

 

  • permission_classes = [IsAuthenticated]
    • 이거 하나만으로 "인증됨"이 아니라면 접근을 제한하게 돼요!

 

 

바~로!

접근이 제한됨을 알 수 있어요!

 

{
    "detail": "Authentication credentials were not provided."
}

접근하기

Token을 이용해서 인증하기

  • Token 정보는 요청 Header에 담아서 전달한다.
  • postman은 보다 편리하게 auth를 담을 수 있도록 편의 기능을 제공한다.
더보기

 

이전에 refresh 받았던 토큰에 access 키를

 

 

  1. Article 목록 조회로 돌아와서
  2. Authorization을 눌러주면 토큰 값을 넣어줄 칸이 생겨요
  3. Auth Type 중에 Bearer Token을 선택해주세요
  4. 그리고 Token 박스에 access 키를 입력해주고 Send를 눌러주면!
  5. Article 목록이 조회됩니다!

 

Bearer??
  • Bearer
    • JWT 혹은 OAuth에 대한 토큰임을 명시하는 것이다. (RFC 6750)
    • 이 값도 특정 단어로 변경할 수 있으나 권장하지 않는다.

Token 유저정보 가져오기

print("현재 유저의 유저 네임:", request.user.username)
  • request.user
    • User 객체 접근가능
    • 나머지는 모두 Django 내부에서 처리

+ Recent posts