개발학습일지

[Restful API] Flask에서 JWT 사용 _ 토큰을 적용한 로그인, 로그아웃 API 본문

Restful API

[Restful API] Flask에서 JWT 사용 _ 토큰을 적용한 로그인, 로그아웃 API

처카푸 2024. 5. 22. 17:56

Flask에서 JWT 사용한 로그인 / 로그아웃 API

 

1. 로그인 API 만들기

- DB에 저장되어 있는 유저일 때 비밀번호가 일치하면 로그인이 되도록 코드를 작성한다.

- 생성된 토큰으로 처리하는 코드를 작성

1-1. 포스트맨에서 로그인 API 만들기

- 회원가입 정보 입력한다.

포스트맨 로그인 API

1-2. app.py Entry point에 경로 만들기

# 경로(path)와 리소스(API 코드)를 연결한다.
# Entry point
api.add_resource( UserRegisterResource , '/users/login' )

1-3. resources/user.py에 API 코드 작성

import datetime
from email_validator import EmailNotValidError, validate_email
from flask import request
from flask_jwt_extended import create_access_token
from flask_restful import Resource
from mysql.connector import Error

from mysql_connection import get_connection
from utils import check_password, hash_password


class UserLoginResource(Resource) :

    # 로그인 하는 API
    def post(self) :

        # 1. 클라이언트에게 데이터 받기
        data = request.get_json()

        if 'email' not in data or 'password' not in data :
            return {'result':'fail'}, 400
        if data['email'].strip() == '' or data['password'].strip() == '' :
            return {'result':'fail'}, 400

        # 2. DB 로 부터 email에 해당하는 유저 정보를 가져온다.
        try :
            connection = get_connection()

            query = '''
                    select *
                    from user
                    where email = %s;'''
            recode = (data['email'],)

            cursor = connection.cursor(dictionary=True)
            cursor.execute( query, recode )

            # fetchall 은 리스트로 값을 가져온다. 그래서 커서를 딕셔너리는 트루로 해줌
            # 딕셔너리로 하는 이유 : 키, 벨류로 데이터 가져오는게 편하기 때문이다.
            result_list = cursor.fetchall()

            print(result_list)
            
            cursor.close()
            connection.close()

        except Error as e:
            if cursor is not None :
                cursor.close()
            if connection is not None :
                connection.close()
            return {"result":"fail", "error":str(e)}, 500

        # 3. 회원인지 확인한다. 아니면 클라이언트에게 리턴
        # API 명세서에 에러코드 설명을 적어준다.
        if result_list == [] :
            return {"result":"fail"}, 401

        # 4. 비밀번호를 체크한다.
        # 유저가 입력한 비밀번호 data['password']
        # DB에 암호화된 비번 result_list[0]['password']
        isCorrect = check_password(data['password'], result_list[0]['password'])
        if isCorrect == False :
            return {"result":"fail"}, 401

        # 5. 유저아이디를 가져온다.
        user_id = result_list[0]['id']

        # 6. JWT 토큰을 만든다.
        access_token = create_access_token(user_id)

        # 7. 클라이언트에 응답한다.
        return {"result":"success", "access_token":access_token}, 200

1-4. 포스트맨에서 로그인 잘 되는지 테스트

- success와 함께 암호화된 인증토큰이 나온다.

포스트맨 로그인 완료

 

 

2. 로그아웃 API 만들기

- 생성된 토큰으로 처리하는 코드를 작성

- 토큰 정보를 받아와야 하기 때문에 app.py에 환경 변수를 세팅해야 한다.

# JWT 환경 변수 설정한 것 인식해라 라는 코드
# app.py의 임포트 항목들 밑에 작성한다.
app.config.from_object(Config)

2-1. 포스트맨에서 로그아웃 API 만들기

- 헤더 정보에 로그아웃 할 토큰 정보 입력한다.

토큰 정보 입력

2-2. app.py Entry point에 경로 만들기

# 경로(path)와 리소스(API 코드)를 연결한다.
# Entry point
api.add_resource( UserRegisterResource , '/users/logout' )

2-3. resources/user.py에 API 코드 작성

- 로그아웃 된 토큰을 저장할 셋(set)을 만는다 (유저파일에 만든다)

# 로그아웃된 토큰을 저장할, set 을 만든다.
jwt_blacklist = set()

class UserLogoutResource(Resource) :

    # 로그아웃 하는 API
    @jwt_required()
    def delete(self) :
        
        jti = get_jwt()['jti']
        jwt_blacklist.add(jti)
        
        return

- *app.py에 로그아웃된 토큰으로 요청하는 경우 처리하는 코드작성한다

# 로그아웃된 토큰으로 요청하는 경우, 처리하는 함수 작성
# 로그아웃된 토큰인지 확인하는 것
@jwt.token_in_blocklist_loader
def check_if_token_is_revoked(jwt_header, jwt_payload):
    jti = jwt_payload['jti']
    return jti in jwt_blacklist

 

2-4. 포스트맨에서 로그아웃 잘 되는지 테스트

- 로그아웃하고 토큰이 필요한 서비스가 실행되는지 확인

- 로그아웃한 토큰으로 서비스 실행 시 오류 코드

로그아웃 토큰

 

 


* JWT 라이브러리에서 제공하는 함수들

- create_access_token() : 토큰으로 만든다. _ 회원가입 또는 로그인 API에서 일반적으로 사용한다.

- @jwt_required(optional=False) : 무조건 토큰 필요하다.라는 의미, 옵셔널을 True로 하면 상관없다는 뜻이다.

- get_jwt_identity() : 토큰을 다시 원본데이터로 변환시켜서 가져온다.

 

* 에러코드 401 설명 사이트

https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401

 

* @jwt.token_in_blocklist_loader 메뉴얼 참고 사이트

https://flask-jwt-extended.readthedocs.io/en/stable/blocklist_and_token_revoking.html