Skip to content

JWT와 인증 시스템에 대한 이해도를 높이기 위해 Python, Flask를 활용해 회원 인증 시스템을 구현하였습니다.

Notifications You must be signed in to change notification settings

Rizingblare/AuthSystem2022-Rizingblare

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

인증 시스템

개요

JWT과 Python 라이브러리를 이용한 유저 인증 시스템 구현

기술스택

  • Python 3.9.13
  • Flask 2.2.2
  • JWT
  • MariaDB

제공기능

기능 설명
회원가입 필요한 정보를 입력받아 신규 회원 등록
회원관리 로그인 인증을 거친 후에 회원들의 개인정보를 열람하고 탈퇴시킬 수 있는 기능

사용한 라이브러리

  • Flask 2.2.2
  • PyJWT 2.6.0
  • bcrypt 4.0.1
  • mariadb 1.1.5.post3

화면 UI

  • 로그인 페이지 image

  • 회원가입 페이지 image

  • 회원 페이지 image

  • 유저 관리 페이지 image

  • 메세지 전달 페이지 image

구현

JWT 인증

@app.route('/login', methods=['POST'])
def login():
    ...
    
    # 해당 로그인 요청의 ID와 PW가 DB에 존재하는지, 일치하는지 확인
    conn = get_db_connection()
    cur = conn.cursor()

    query = f"SELECT password FROM auth.password WHERE user_no = (SELECT user_no FROM member.user WHERE user_id = '{userID}');"
    cur.execute(query)
    conn.commit()

    subscribed = cur.fetchall()
    ...
    
    if subscribed:
        db_user_password = subscribed[0][0]

    else:
        return render_template('message.html', msg = f"오류: 등록되지 않은 아이디입니다.")

    # 입력받은 비밀번호와 DB에 저장된 유저 비밀번호의 일치여부 확인
    if bcrypt.checkpw(userPW.encode('utf-8'), db_user_password.encode('utf-8')):
    
        access_token, access_timeout_msg = token_generator(0, userID)
        refresh_token, refresh_timeout_msg = token_generator(1, userID)
        
        # Refresh Token은 DB에 저장, 이미 있다면 삭제
        ...
        query = f"UPDATE auth.password SET refresh_token = '{refresh_token}' WHERE user_no = (SELECT user_no FROM member.user WHERE user_id = '{userID}');"
        ...
        
        # 클라이언트의 cookie에 access token 값을 전달
        res = make_response(render_template('authorized.html', userID = userID, r_msg = refresh_timeout_msg))
        res.set_cookie('access_token', access_token)
        return res
    
    else:
        return render_template('message.html', msg = f"오류: 잘못된 비밀번호 입니다.")

JWT 인가

@app.route('/manage', methods=['GET'])
def manage():
    access_token = request.cookies.get('access_token')
    try:
        payload = jwt.decode(access_token, options={"verify_signature": False})

    except jwt.InvalidTokenError:
        # 로그인 인증 없이 서비스에 접근하는 경우
        return render_template('message.html', msg = f"오류: 유효하지 않은 접근입니다!")

    else:
        ...

        token_expired = token_exp_verify(access_token)

        if token_expired:
            # 1) access token의 유효기간이 만료된 경우
            if refresh_token:
                token_expired = token_exp_verify(refresh_token)

                if token_expired:
                    # 1-1) refresh token의 유효기간이 만료된 경우
                    return render_template('message.html', msg = f"오류: 모든 토큰이 만료되어 재로그인이 필요합니다!")

                else:
                    # 1-2) refresh token이 아직 유효한 경우
                    access_token, access_timeout_msg = token_generator(0, userID)
                    print("access_token을 재발급하였습니다!")

                    # 이후 서비스 정상 처리
                    user_dict = get_user_info()
                    res = make_response(render_template('user-manage.html', userID = userID, user_dict = user_dict))
                    res.set_cookie('access_token', access_token)

                    return res
            else:
                return render_template('message.html', msg = f"오류: refresh 토큰이 존재하지 않습니다!")

        else:
            # 2) access token이 유효한 경우
            if refresh_token:
                token_expired = token_exp_verify(refresh_token)

                if token_expired:
                    # 2-1) refresh token의 유효기간이 만료된 경우
                    refresh_token, refresh_timeout_msg = token_generator(1, userID)
                    
                    ...

                    query = f"UPDATE auth.password SET refresh_token = '{refresh_token}' WHERE user_no = (SELECT user_no FROM member.user WHERE user_id = '{userID}');"
                    cur.execute(query)
                    
                    ...

                    print("refresh_token을 재발급하였습니다!")

                    # 이후 서비스 정상 처리 (유저 관리 페이지로 이동)
                    user_dict = get_user_info()
                    return render_template('user-manage.html', userID = userID, user_dict = user_dict)

                else:
                    # 2-2) refresh token이 아직 유효한 경우
                    # 이후 서비스 정상 처리

                    user_dict = get_user_info()
                    return render_template('user-manage.html', userID = userID, user_dict = user_dict)

            else:
                return render_template('message.html', msg = f"오류: refresh 토큰이 존재하지 않습니다!")

수정 & 보완사항 및 고민

  • 상수, Config 파일

    SECRET_KEY와 같은 상수(환경 변수) 값들이나 DB Config 값들을 별도의 파일로 저장하고
    필요할 때 읽어와서 쓰는 식으로 관리하고 싶은데 구체적으로 어떤 값들을 어떻게 묶어서 관리해야 할지
    판단이 서질 않아서 실행에 옮기지 않았음

  • 파이썬 함수 오버라이딩

    인증이 필요한 서비스에 토큰을 검증하는 프로세스를 Python Decorator와 같은 문법을 사용하여
    오버라이딩(?)해서 관리하고 싶은데 사용법이 익숙하지 않아서 적용하지 못했음

  • html 기초

    UI 웹 디자인이 익숙하지 않아서 처음 html 파일을 생성할 때,
    구조를 비효율적으로 잡아서 이후에 CSS를 적용하느라 시간 낭비를 많이 하였음.
    html을 작성할 때 기본적으로 권장되는 사항들에 대한 학습이 필요한 것 같음.

  • 구현, 활용

    라이브러리 사용을 지양하는 것이 어느 정도가 적정 수준인지 모르겠음.
    JWT도 직접 JSON 파일로 생성하고, 패스워드 hash & salt 함수도 직접 구현하여야 하는 것인지,
    모든 것을 다 직접 구현하는 것이 이상적인지 아니면 학습의 차원에서도 오히려 비효율적이진 않은지
    사이에서 고민을 많이 하였음

  • 가상 환경, 라이브러리, 배포

    사용한 가상환경 파일들과 설치한 라이브러리들을 어떤 방식으로 저장해야할 지 모르겠음.
    더 구체적으로 이야기하면 어떤 파일들을 git에 저장해서 배포하고
    어떤 파일들을 ignore해야할지
    판단이 서질 않음.

  • MSA, 도커

    서비스 요청을 역할 단위로 묶어서 처리하는 서버를 다중화한다는
    MSA의 구조와 의도는 어느 정도 이해했지만 구체적으로 어떤 프로세스로 이를 구현할 수 있는지,
    도커Docker에서 이미지를 어떻게 저장해서 컨테이너를 어떻게 발생시켜야 하는지
    걸림돌에 가로막히는 부분이 너무 많다, 사용법도 아직 낯설고.

  • 네트워크, 포트, 클라이언트

    네트워크에 대한 이해가 부족한 것 같기도 하다. 한 PC에서 여러 서버를 작동시키려고 할때,
    필요한 포트에 대한 개념도 그렇고, cookie나 session, 그리고 cash와 같은 클라이언트 측이 소유하고 있는 것,
    할 수 있는 것과 운용할 수 있는 것에 대한 개념도 거의 없다는 것을 느낌.

About

JWT와 인증 시스템에 대한 이해도를 높이기 위해 Python, Flask를 활용해 회원 인증 시스템을 구현하였습니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published