본문 바로가기

프로젝트

[TIL] 20240523 69일차

base.html 작성 - 1

[기능 구현]

async function tokenRefresh() {
    const refresh = localStorage.getItem('refresh_token');
    const response = await fetch('{% url 'accounts:refresh_token' %}', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            refresh
        })
    });

    const data = await response.json();
    if (response.ok) {
        localStorage.setItem('access_token', data.access);
        localStorage.setItem('refresh_token', data.refresh);
    } else {
        alert('로그인 정보가 없습니다. 로그인 페이지로 이동합니다.');
        window.location.href = "{% url 'accounts:login_page' %}";
    }
}

refresh 토큰을 이용해 새로운 access token과 refresh token을 발급받고 기존의 refresh 토큰을 만료처리하는 함수이다.

token을 다루는 동작은 accounts:refresh_token api 에서 처리해주는 것이고, api response에 따라 local storage에 token들을 저장하거나 로그인 페이지로 이동하도록 한다.

access token과 refresh token을 local storage에 저장할 경우 xss(Cross Site Script) 공격에 취약한 보안 취약점이 있어 다른 방식으로 구현하기로 결정했다.

async function logout() {
    const refresh = localStorage.getItem('refresh_token');
    const response = await fetch('{% url 'accounts:logout' %}', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            refresh
        })
    });

    if (response.ok) {
        localStorage.removeItem('access_token');
        localStorage.removeItem('refresh_token');
        localStorage.removeItem('username');
        window.location.href = "{% url 'test_base_view' %}";
    } else {
        alert('로그인 정보가 없습니다. 로그인 페이지로 이동합니다.');
        window.location.href = "{% url 'accounts:login_page' %}";
    }
}

refresh token을 body에 담아 accounts:logout api를 호출하면 해당 refresh token을 서버에서 만료시키고 클라이언트의 local storage에 있는 access token 과 refresh token, username을 삭제한다. refresh token이 없거나 만료된 상태여서 response.ok가 아닌 경우 로그인 페이지로 이동한다.

access token과 refresh token을 local storage에 저장할 경우 xss(Cross Site Script) 공격에 취약한 보안 취약점이 있어 다른 방식으로 구현하기로 결정했다.

async function temp({url, method, is_token, body = null}) {
    const headers = {};

    if (is_token) {
        await tokenRefresh();
        const access = localStorage.getItem('access_token');
        headers['Authorization'] = `Bearer ${access}`
    }

    const data = {
        method: method,
        headers: headers,
    }

    if (body) {
        data["body"] = JSON.stringify(body)
    }

    console.log(url);
    const response = await fetch(`${url}`, data);

    return response.json();
}

api 주소와 요청 method, 요청 시 access token이 필요한지 여부, body를 인자 값으로 서버에 api 요청을 보내고 response를 return 해주는 함수이다. 해당 함수를 base.html에 정의하여 base.html을 extends로 불러오는 다른 템플릿에서도 사용할 수 있도록 하기 위해 만들었다.

access token과 refresh token을 local storage에 저장할 경우 xss(Cross Site Script) 공격에 취약한 보안 취약점이 있어 다른 방식으로 구현하기로 결정했다.

function Search() {
    let search_option_tag = document.getElementById('search_option');
    let search_input_tag = document.getElementById('search_input');

    if (!search_option_tag.value) {
        window.location.href = "{% url 'test_search_view' %}";
    } else {
        window.location.href = "{% url 'test_search_view' %}" + `?${search_option_tag.value}=${search_input_tag.value}`;
    }
}

검색 옵션과 검색 단어 입력값을 가져와서 쿼리 스트링 형태로 변환한 후 리다이렉션 해주는 함수이다. 검색 옵션을 지정하지 않았을 경우에는 별도의 쿼리 스트링을 붙이지 않는다.

function isLogin() {
    const refresh = localStorage.getItem('refresh_token');
    const username = localStorage.getItem('username');
    const navbar_login_userinfo = document.getElementById('navbar_login_userinfo');
    const navbar_logout_userinfo = document.getElementById('navbar_logout_userinfo');
    const navbar_login_userinfo_username = document.getElementById("navbar_login_userinfo_username");

    if (refresh) {
        tokenRefresh();
        navbar_login_userinfo.style.display = 'block';
        navbar_login_userinfo_username.innerText = username;
        navbar_logout_userinfo.style.display = 'none';
    } else {
        navbar_login_userinfo.style.display = 'none';
        navbar_logout_userinfo.style.display = 'block';
    }
}

window.onload = isLogin;

페이지에 접속했을 때 사용자가 로그인한 상태인지 아닌지를 판별하고, 로그인 상태에 따라 navbar의 특정 요소를 보여주거나 숨겨주는 함수이다.

access token과 refresh token을 local storage에 저장할 경우 xss(Cross Site Script) 공격에 취약한 보안 취약점이 있어 다른 방식으로 구현하기로 결정했다.

 

 

JS 매개변수 명시적 표시 문제

[기타사항]

 

[발단]

Python 에서 함수를 정의하고, 파라미터를 명시적으로 사용할 때 아래와 같이 작성한다.

def sum(a, b):
		return a + b

# 함수 호출
print(sum(a=2, b=5))
# 출력: 7

‘<파라미터> = <전달할 인자 값>’ 형태로 작성한다.

python 처럼 js에서도 작성을 하니, 전달한 인자가 부족하다는 error를 출력했다.

[해결]

JS에서는 이를 object로 묶어서 처리해야 한다.

function sum({a, b}) {
		return a + b;
}

const sum_ab = temp({
    a: 2,
    b: 5
});

각 파라미터와 전달할 인자 값을 key - value 형태로 작성한 하나의 object로 묶어야 한다.

이는 함수를 정의할 때도 각 파라미터를 object 형태로 묶어 작성해야 한다.

 

 

Token을 어떻게 다룰 것인가 - 1

[기술적 의사결정]

 

base.html을 작성

→ base.html 에서 username을 출력해야 하는 부분이 있었다 (navbar 요소)

→ username을 기존처럼 request.user.username으로 가져오려고 하니 문제가 발생

test_02 계정으로 로그인했으나 username은 admin으로 출력되거나(캐싱된 데이터) 아무것도 안뜨는 문제 발생

→ user 정보를 받아올 수 없어 어떻게 처리해야하는지 고민하던 중 access token과 refresh token 처리에 대해서도 고민하게 됨

→ 그 와중에 프론트 세션이 있어 참여했더니 access token은 메모리로 다루고, refresh token은 http-only 설정된 쿠키에 저장을 하라고 함

→ 기존에 작성한 코드는 access token과 refresh token 모두 로컬 스토리지에 저장하는 방식인데, 로컬 스토리지는 xss 보안 취약점이 있어 사용을 지양하라고 하니 혼란이 옴

 

 

div 클래스를 지정할 때 style 속성이 무시되는 문제

[기타사항]

 

div 태그에 d-flex 클래스를 지정하고, display 속성을 부여했다.

<div id="navbar_login_userinfo" class="d-flex" style="display: none; align-content: center"></div>

이렇게 작성했을 때 “display: none;” 속성이 적용되지 않았다.

이유는 class=”d-flex” 속성을 부여했을 때 d-flex 클래스에서 정의한 style 속성이 있기 때문에 무시되는 것이었다.

따라서 div 태그의 클래스 속성을 빼서 문제를 해결했다.

<div id="navbar_login_userinfo" style="display: none; align-content: center"></div>

'프로젝트' 카테고리의 다른 글

[TIL] 20240524 70일차  (0) 2024.05.24
20240520 ~ 20240524 14주차 정리  (0) 2024.05.24
[TIL] 20240522 68일차  (0) 2024.05.22
[TIL] 20240521 67일차  (0) 2024.05.21
[TIL] 20240520 66일차  (0) 2024.05.20