IFIF3526
IFIF3526
IFIF3526
전체 방문자
오늘
어제
  • 분류 전체보기 (54)
    • Android Studio (7)
    • AWS (4)
    • Machine Learning (3)
    • Node.js (2)
    • 언어 (29)
      • Python (13)
      • JAVA (7)
      • JavaScript (3)
      • SQL (6)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
IFIF3526

IFIF3526

카테고리 없음

Flask JWT | Register / Login API에서 토큰 생성 및 유효기간 만료

2022. 6. 20. 17:39

JSON Web Token 에 대한 설명

사용자 인증 (User Authentication)에 사용되며 사용자와 서버에 전달된다.

  • JWT는 header, payload, signature 각각 base64 encoding 한 후 concat한 문자열이다.
    • 토큰에 포함된 내용들은 암호화되지 않아서 누구나 확인 할 수 있다.
    • 토큰에 포함되는 여러 종류의 필드들이 용도별로 미리 정의 되어있어서 일관성 있는 사용이 가능하다.
    • signature를 이용하여 해당 토큰이 실제로 원래 발급자가 발급했던 유효한 토큰인지 검증을 할 수 있다.
    • signature 생성을 위한 알고리즘을 원하는 대로 선택이 가능하다. (ex: RS256, HS256 등)
    • 실제 생성된 JWT 스트링의 샘플은 https://jwt.io 에서 손쉽게 확인 가능하다.

https://www.letmecompile.com/api-auth-jwt-jwk-explained/

 

Flask-JWT-Extended 라이브러리 설치하기.

라이브러리 설치

$ pip install flask-jwt-extended

https://flask-jwt-extended.readthedocs.io/en/latest/options/

 

Configuration Options — flask-jwt-extended 4.4.1 documentation

Configuration Options You can change many options for this extension works via Flask’s Configuration Handling. For example: app.config["OPTION_NAME"] = option_value General Options: JWT_TOKEN_LOCATION Where to look for a JWT when processing a request. Th

flask-jwt-extended.readthedocs.io

 

config.py 파일에, JWT 암호화를 위한 변수 추가

다음을 셋팅한다.

  • -SECRET_KEY : 암호화 하기 위한 키. 노출되면 안된다.

예

 

config.py

SECRET_KEY = 'super-secret-key'

 

회원가입 / 로그인 API에 토큰 생성하기

class UserRegisterResource(Resource) :
    def post(self) :
       
        # {
        #     "username" : "홍길동",
        #     "email" : "abc@naver.com",
        #     "password" : "1234"
        # }

        # 클라이언트가 body 에 보내준 json 을 받아온다.
        data = request.get_json()

        # 2. 이메일 주소형식이 제대로 된 주소형식인지
        # 확인하는 코드 작성.
        try :
            validate_email(data['email'])

        except EmailNotValidError as e:
            # email is not valid, exception message is human-readable
            print(str(e))
            return {"error" : str(e)}, 400
       
        # 3. 비밀번호의 길이가 유효한지 체크한다.
        if len(data['password']) < 4 or len(data['password']) > 12 :
            return {'error' : '비밀번호 길이를 확인하시오.'}, 400

        # 4. 비밀번호를 암호화 한다.
        # data['password']

        hashed_password = hash_password(data['password'])

        # 5. 데이터베이스에 회원정보를 저장한다.
       
        try :
            # 데이터 업데이트
            # 1. DB에 연결
            connection = get_connection()

            # 2. 쿼리문 만들기

            query = '''insert into user
                    (username, email, password)
                    values
                    (%s, %s, %s);'''

            record = (data['username'], data['email'], hashed_password)
           
            # 3. 커서를 가져온다.
            cursor = connection.cursor()

            # 4. 쿼리문을 커서를 이용해서 실행한다.
            cursor.execute(query, record)

            # 5. 커넥션을 커밋해줘야 한다 => DB에 영구적으로 반영하라는 뜻
            connection.commit()

            # 5-1. DB에 저장된 user_id 값 가져오기.
            user_id = cursor.lastrowid

            # 6. 자원 해제
            cursor.close()
            connection.close()

        except mysql.connector.Error as e :
            print(e)
            cursor.close()
            connection.close()
            return {'error' : str(e)}, 503

        # user_id 값을 바로 보내면 안되고,
        # JWT 로 암호화 해서 보내준다.

        # 암호화를 하는 방법
        access_token = create_access_token(user_id)

        return {'result' : 'success', 'access_token' : access_token}, 200

class UserLoginResource(Resource) :
    def post(self) :

        # 1. 클라이언트로부터 body로 넘어온 데이터를 받아온다.

        # {
        #     "email" : "abc@naver.com",
        #     "password" : "1234"
        # }

        data = request.get_json()

        # 2. 이메일로, DB에 해당 이메일과 일치하는 데이터를 가져온다.
        try :
            connection = get_connection()

            query = '''select *
                    from user
                    where email = %s;'''

            record = (data['email'], )

            #select 문은, dictionary = True를 해준다.
            cursor = connection.cursor(dictionary = True)

            cursor.execute(query, record)

            # select 문은, 아래 함수를 이용해서, 데이터를 가져온다.
            result_list = cursor.fetchall()

            print(result_list)
           
            # 중요, DB에서 가져온 timestamp는
            # 파이썬의 datetime 으로 자동 변환된다.
            # 문제는, 이 데이터를 json으로 바로 보낼 수 없으므로
            # 문자열로 바꾼뒤 저장하여 보낸다.

            i = 0
            for record in result_list :
                result_list[i]['created_at'] = record['created_at'].isoformat()
                result_list[i]['updated_at'] = record['updated_at'].isoformat()
                i = i + 1

            cursor.close()
            connection.close()

        except mysql.connector.Error as e :
            print(e)
            cursor.close()
            connection.close()
            return{'error' : str(e)}, 503

        # 3. result_list 의 행의 갯수가 1개이면, 유저 데이터를 정상적으로 받아온 것이고,
        # 행의 갯수가 0이면, 클라이언트가 요청한 이메일은 회원가입이 되어 있지 않은 이메일이다.
        if len(result_list) != 1 :
            return {'ERROR' : '등록되지 않은 이메일입니다.'}, 400

        # 4. 비밀번호가 유효한지 확인한다.
        user_info = result_list[0]

        # data['password'] 와 user_info['password']를 비교한다.
        check = check_password(data['password'], user_info['password'])

        if check == False :
            return {'ERROR' : '비밀번호가 일치하지 않습니다.'}

        # 엑세스 토큰요청하고, 해당 엑세스 토큰을 1분후 만료시킨다.
        access_token = create_access_token(user_info['id'], expires_delta=datetime.timedelta(minutes=1))

        return {'result' : 'success', 'access_token' : access_token}, 200
    IFIF3526
    IFIF3526
    배운 것을 정리하며, 복습을 위해 정리하는 공간입니다...

    티스토리툴바