JWT (Json Web Token) 의 개념과 Flask에서 JWT의 사용

@developer/takealittle.time·2024년 9월 12일
1

Jungle

목록 보기
1/21
post-thumbnail

JWT(Jason Web Token)

Json 객체에 인증에 필요한 정보들을 담아 비밀키로 서명한 토큰.

  • 인터넷 표준 방식
  • 인증 (Authentication), 인가(Authorization)에 사용.

JWT 사용의 예시를 나타내면 다음과 같다.

<로그인 이전>

<로그인 이후>


JWT 사용의 이유

HTTP의 특성

  • Connectionless: 한 번 통신이 이뤄지고 난 후 연결 끊김.
  • Stateless: 이전 상태를 기억하거나 유지하지 못 함.
    -> 인증된 사용자가 어느 기간동안 재인증 하지 않아도 되는. '로그인 유지' 의 필요

검증의 구현

  • JWT 사용의 근본적 목적은 '검증(인가)'에 있다.
  • JWT는 구조적으로 서명을 검증해주어 출처를 확실하게 해준다. 이를 통해 어떤 정보에 대해 사용자가 접근하려고 할 때, 권한이 검증된 사용자인지 확실하게 구별할 수 있도록 해준다.

JWT의 구조


JWT는 Json형식으로 위와 같은 구조를 가지고 있으며, 각각 다음과 같은 정보들을 가진다.

  • Header: 알고리즘 방식 (alg), 토큰의 타입(typ)
  • Payload: 토큰에서 사용할 정보
  • Signature: 토큰 인코딩, 유효성 검증에 필요한 암호화 정보.

🚩 Python Flask에서 Flask-JWT-Extended를 사용해 JWT 토큰 실습

0. flask_jwt_extended 설치 및 초기 설정

pip install flask-jwt-extended

pip 환경에서 위와 같이 flask-jwt-extended를 설치 해준다.

from flask_jwt_extended import *

app = Flask(__name__)
app.config['JWT_TOKEN_LOCATION'] = ['cookies']  # 쿠키에서 JWT를 찾도록 설정
app.config['JWT_ACCESS_COOKIE_NAME'] = 'access_token'
app.config.update(
    JWT_SECRET_KEY = "TEST"
)

위와 같이 JWTManager의 초기설정을 해 두었다.

* 'JWT_TOKEN_LOCATION'을 따로 설정해주지 않으면, flask_jwt_extended에서는 JWT를 찾아오거나 발급할 때 기본적으로 HTTP의 Authorization 헤더를 사용한다.

나는 JWT Token을 쿠키 스토리지에 저장하고 사용할 것이기 때문에, 위와 같이 설정하였다.

1. 로그인 구현

app.py

from flask import Flask, jsonify, request, render_template, url_for, redirect
from pymongo import MongoClient
from flask_jwt_extended import *
1
client = MongoClient('localhost', 27017)
db = client.users
jwt = JWTManager(app)

# 로그인 기능
@app.route('/api/signin', methods=['POST'])
def login():
    userId = request.form['id']
    userPw = request.form['pw']
    
    # DB에 있는 데이터와 비교하기
    user = db.userInfo.find_one({'id' : userId})
    if user:
        if not verify_password(user['pw'], userPw):
            return jsonify({'result' : 'failed', 'msg' : '로그인이 실패했습니다.'})
        
        # JWT 토큰 생성 (유효시간 60분)
        expires = datetime.timedelta(minutes=60)
        access_token = create_access_token(
            identity = userId, 
            expires_delta = expires,
            additional_claims={
                'name': user['nickName'],
                'introduced': user['hasIntroduce'],
                'profile_image':user.get('profile','')
            }
        )
        response = jsonify({'result' : 'success', 'msg' : '정상적으로 로그인이 되었습니다.', "token" : access_token})
        response.set_cookie('access_token', access_token, secure=True, samesite='Lax') # 쿠키 보안 설정
        return response
    else:
        return jsonify({'result' : 'failed', 'msg' : '로그인이 실패했습니다.'})

위에서 실습한 코드를 아래에서 차근차근 살펴보도록 하자.
정상적으로 로그인이 된 경우, 다음과 같이 jwt 토큰을 발급한다.

# JWT 토큰 생성 (유효시간 60분)
        expires = datetime.timedelta(minutes=60)
        access_token = create_access_token(
            identity = userId, 
            expires_delta = expires,
            additional_claims={
                'name': user['nickName'],
                'introduced': user['hasIntroduce'],
                'profile_image':user.get('profile','')
            }
        )
        response = jsonify({'result' : 'success', 'msg' : '정상적으로 로그인이 되었습니다.', "token" : access_token})
        response.set_cookie('access_token', access_token, secure=True, samesite='Lax') # 쿠키 보안 설정
        return response
  • 위에서 expires는 토큰의 유효 시간으로, 코드에서는 60분을 설정했다. 유효한 시간이 지나면 토큰이 자동으로 만료되며, 페이지에서는 로그아웃 된다.
  • 로그인 이후 사용자의 정보를 여러 페이지에서 이용할 수 있도록 작성하고 싶어, additional_claims에 해당 정보들을 추가했다.
  • response.set_cookie 쿠키 스토리지에 해당 토큰을 저장하도록 했다.

signin.html

function submitSignIn() { //로그인 버튼 눌렀을 때

				let id_give = $('#input-id').val();
				let pw_give = $('#input-pw').val();

				//서버 API로 전달.
                $.ajax({
                    type: "POST",
                    url: "/api/signin",
                    data: {id: id_give, pw: pw_give},
                    success: function (response) { 
                        if (response["result"] != "success"){
                            alert(response["msg"]);
                        }

                        else {
                            alert(response["msg"]);
                            window.location.href = '/';
                        }
                    }
                })
			}

2. 로그인 이후 사용 :: 검증

app.py

@app.route('/')
#jwt_token을 확인한다.
@jwt_required(optional=True)
def home():
    logged_in = False
    current_identity = None
    name = None
    is_introduced = None

	#get_jwt()를 통해 토큰을 쿠키에서 꺼내 jwt_check에 할당한다.
    jwt_check = get_jwt()
    
    if jwt_check:
    	#jwt 토큰이 있는 경우, 값들을 꺼내 다음과 같이 할당한다.
        logged_in = True
        current_identity = jwt_check["sub"]
        name = jwt_check["name"]
        is_introduced = jwt_check["introduced"]
        
    # 템플릿에 값들을 함께 전달한다. 
    return render_template('index.html', 
                           logged_in=logged_in, 
                           name=name, 
                           is_introduced=is_introduced, 
                           current_identity=current_identity)

로그인 이후 사용되는 페이지 (즉, 인가가 필요한 페이지)에 대해
@jwt_required() 를 붙여주어 jwt 토큰을 확인할 수 있다.
이 때, @jwt_required(optional=True) 처럼 작성해주면, jwt 토큰이 없어도 일단 페이지에 접근할 수 있다.

  • 참고: 값들을 json으로 묶어서 script로 전달할 수도 있다. https://karl27.tistory.com/105 (이 방법이 보기에 더 깔끔해보인다. 다음에 한 번 시도해보려 한다.)

  • render_template()은 SSR(서버 사이드 렌더링) 방식으로 템플릿을 렌더링 해 호출한다. 이에 대해서는 CSR과 SSR에 대해 한 번 더 포스팅하려 한다.

index.html

<script>  
        const loggedIn = "{{ logged_in }}";
        const name = "{{ name }}";  // 문자열인 경우 따옴표로 감싸기
        const isIntroduced = "{{is_introduced}}";
        const currentIdentity = "{{ current_identity }}";

        // 이제 JavaScript에서 사용할 수 있음
        console.log("Logged in:", loggedIn);
        console.log("Name:", name);
        console.log("Is Introduced:", isIntroduced);
        console.log("Current Identity:", currentIdentity);
</script>

위처럼 render_template()을 통해 전달받은 값을 JavaScript에서 선언 해 이용해줄 수 있다.


✅ JWT TOKEN 직접 확인하기

https://jwt.io/ 에 들어가 token 값을 입력하면 현재 자신이 사용 중인 token이 어떤 구조로 이루어졌는지 쉽게 확인할 수 있다.

** 참고 :: jwt 토큰 확인하는 법 (토큰을 쿠키에 저장한 경우)


Chrome 브라우저를 통해 실습하는 경우, F12 (개발자 도구) 에서 Application - Cookies 의 쿠키 스토리지를 확인하면 본인의 token을 확인할 수 있다.

profile
능동적으로 사고하고, 성장하기 위한. 🌱

0개의 댓글

관련 채용 정보