AI 웹 개발 과정/DRF 특강

DRF 커스텀 JWT 클레임 , 프론트엔드에서 JWT 환경 구성

만 기 2022. 8. 11. 11:35

 

JWT claim custumizing

serializer를 활용하여 simplejwt에서 제공하는 기본 정보 이외에 포함하고 싶은 정보를 payload에 추가적으로 넣을 수 있다.

 

1) user/jwt_claim_serializer.py 파일 생성 후 작성

# TokenObtainPairSerializer를 임포트해서 새로생성할 serializer에 상속 시켜줘야한다.
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

# TokenObtainPairSerializer를 상속하여 클레임 설정
class SpartaTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user): # request 했을때 들어오는 user
	# 가져온 user정보로 token 생성
        token = super().get_token(user)

        # 토큰에 추가할 필드 설정하기.
        token['id'] = user.id
        token['username'] = user.username

        return token

 

2) serializer를 실행할 view를 user/views.py 에 추가

from user.jwt_claim_serializer import SpartaTokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

...

class SpartaTokenObtainPairView(TokenObtainPairView):
    serializer_class = SpartaTokenObtainPairSerializer

* simplejwt 에서 제공하는 TokenObtainPairView의 serializer_class 변수에 커스텀한 시리얼라이저를 넣어주는 것.

 

3) 생성한 view를 실행할 urls path 추가

user/urls.py

from user.views import SpartaTokenObtainPairView
...

urlpatterns = [
	...
    path('api/sparta/token/', SpartaTokenObtainPairView.as_view(), name='sparta_token'),
]

 

포스트맨에서 해당 url로 토큰을 발급받아 https://jwt.io/ 에서 확인해보면 payload에 username이 추가된 것을 볼 수 있다.

 

 

 

프론트엔드에서 JWT 테스트

로그인 => 유저데이터 받아서 서버에 요청

// 로그인 함수. es6문법
const onLogin = (e)=>{
    // requestAccessToken 함수 비동기통신을 위해 async fetch 사용
    const requestAccessToken = async (url, sendData)=>{
        const response = await fetch(url, {
            headers: {  // 헤더에 담을 데이터 방식
                'Content-Type': 'application/json',
            },
            method: "POST",
            body: JSON.stringify(sendData)  // sendData = loginInfo
        });

        return response.json();
    };

    const data = new FormData(e);
    const loginInfo = {
        "username": data.get("username"),
        "password": data.get("password")
    };

    // then : 요청 후에 응답한 결과, data : 서버가 클라이언트에게 준 응답
    requestAccessToken("/user/api/token/", loginInfo).then((data=>{

        const accessToken = data.access;
        const refreshToken = data.refresh;
        document.querySelector("#access-token").value = accessToken;
        document.querySelector("#refresh-token").value = refreshToken;

        // 서버로 부터 응답받은 accessToken과 refreshToken, payload를 클라이언트 저장소에 저장
        localStorage.setItem("sparta_access_token", accessToken);
        localStorage.setItem("sparta_refresh_token", refreshToken);

        // access token에서 payload를 꺼내는 작업
        // 0 -> header, 1 -> payload, 2 -> VERIFY SIGNATURE
        const base64Url = accessToken.split('.')[1];
        const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
        const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));

        document.querySelector("#payload").value = jsonPayload;

        localStorage.setItem("payload", jsonPayload);
    }));
    return false;
};

 

 

 

LocalStorage

  • 브라우저 내에 존재하는 저장소.
  • 웹 브라우저가 종료되면 사라지는 `SessionStorage` 와는 다르게 브라우저가 종료되어도 저장된 정보가 계속 남아있는 공간이다.
  • 데이터는 `key-value` 로 저장된다.
  • 서버로 부터 응답받은 JWT 정보를 `localStorage` 에 넣어놓고 인가를 필요로 하는 요청을 할 때 `access` 토큰을 `header` 에 담아서 전달하는 방식이다.
// localStorage에 데이터 쓰기
localStorage.setItem("item_key", value);

// localStorage에서 데이터 읽기
localStorage.getItem("item_key");

// localStorage에 키에 맞는 데이터 삭제
localStorage.removeItem("item_key");

// localStorage에 있는 모든 데이터 삭제
localStorage.clear();

// localStorage에 있는 모든 데이터(Key Value 쌍)의 개수
localStorage.length;

 

 

 

Refresh token 이용해서 새로운access token 받기

 // 페이지를 다시 로딩 됐을 때
window.onload = ()=>{
    // localstorage에서 payload를 꺼내온다.
    const payload = JSON.parse(localStorage.getItem("payload"));

    // exp : 토큰 인증유효시간
    // 아직 access 토큰의 인가 유효시간이 남은 경우
    if (payload.exp > (Date.now() / 1000)){
        document.querySelector("#loginForm").setAttribute("style", "display:none");

        document.querySelector("#access-token").value = localStorage.getItem("sparta_access_token");
        document.querySelector("#refresh-token").value = localStorage.getItem("sparta_refresh_token");
        document.querySelector("#payload").value = JSON.stringify(localStorage.getItem("payload"));

    } else {
        // 인증 시간이 지났기 때문에 다시 refreshToken으로 다시 요청을 해야 한다.
        const requestRefreshToken = async (url) => {
            const response = await fetch(url, {
                headers: {
                    'Content-Type': 'application/json',
                },
                method: "POST",
                // body에 refresh token 을 넣어서 재요청
                body: JSON.stringify({
                    "refresh": localStorage.getItem("sparta_refresh_token")
                })}
            );
            return response.json();
        };

        // 다시 인증 받은 accessToken을 localStorage에 저장하자.
        requestRefreshToken("/user/api/token/refresh/").then((data)=>{
            // 새롭게 발급 받은 accessToken을 localStorage에 저장
            const accessToken = data.access;
            document.querySelector("#access-token").value = accessToken;

            // 받은 토큰 다시 localstorage에 저장한다.
            localStorage.setItem("sparta_access_token", accessToken);
            document.querySelector("#refresh-token").value = localStorage.getItem("sparta_refresh_token");
            document.querySelector("#payload").value = JSON.stringify(localStorage.getItem("payload"));

            document.querySelector("#loginForm").setAttribute("style", "display:none");
        });
    }
};

 

 

 

인가된 사용자의 요청

// 인가된 사용자 요청
const onRequestButtonClick = () => {
    const requestAuthData = async () => {
        const response = await fetch("/user/api/authonly/", {
            method:"GET",
            headers: {  // 헤더에 토큰을 담아서 보낸다.
                'Content-Type': 'application/json',
                "Authorization": "Bearer " +localStorage.getItem("sparta_access_token")
            },
        });

        return response.json();
    }
    requestAuthData().then((data)=>{
          document.querySelector("#auth-only").value = data.message;
    })
  };

'AI 웹 개발 과정 > DRF 특강' 카테고리의 다른 글

django-dotenv  (0) 2022.07.30
DRF JWT 사용하기  (1) 2022.07.27
DRF 테스트 코드 작성하기 1  (0) 2022.07.11
DRF 를 이용한 JWT 사용하기  (0) 2022.06.27
DRF 퀴즈2  (0) 2022.06.24