[에러] AJAX와 flask_jwt_extended로 로그인 유지하기

2023. 6. 20. 16:00·IT/에러

발생

flask 기반 웹 프로젝트를 진행하다 해당 문제가 생겼다. 로그인 기능을 구현하며 페이지를 옮겨갈 때마다 쿠키에 저장한 jwt(json web token)을 읽어서 로그인 여부를 확인하려고 했는데, 이때 flask_jwt_extended 라이브러리에서 제공하는 데코레이터 함수 flask_jwt_extended.jwt_required()를 사용하는데 문제가 생겼다.

@app.route("/protected")
@jwt_required()
def protected():
    current_user_id = get_jwt_identity()
    return jsonify(logged_in_as=current_user_id), 200

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

위 코드처럼 @jwt_required()를 사용하면 protected() 함수를 호출하기 위해선 유효한 jwt가 요구된다. 또한 get_jwt_identity() 함수는 jwt에 담긴 사용자 identity값을 리턴하는데, 이 함수를 호출하기 위해선 @jwt_required() 함수가 반드시 호출되어야만 한다.

 

따라서 클라이언트가 '/protected' api를 호출할 때 헤더에 jwt를 담으면 정상적으로 함수를 동작시킬 수 있다.

jwt 발급은 create_access_token()를 사용한다.

access_token = create_access_token(identity=user_id)

문제

그러나 ajax에서 헤더에 값을 추가하여 전송하기 위해선 beforeSend 부분을 추가해야 한다.

function to_ajax(){

    $.ajax({
        type : 'get',
        url : '/test,
        dataType : 'xml',
        beforeSend : function(xhr){
            xhr.setRequestHeader("Authorization","JWT " + token);
        },
        error: function(xhr, status, error){ 
            alert(error); 
        }
        success : function(res){
            alert(res)
        },
    });
}

그러나 필자는 쿠키가 아닌 웹브라우저 상의 변수 token에 jwt를 저장하는 것은 보안 이슈가 있을 것이라 판단했고, 따라서 ajax 헤더에 값을 추가하는 것 말고 다른 방법을 찾기로 했다.

 

솔루션

로그인한 사용자를 식별하기 위해서는 쿠키에 저장된 jwt에 담긴 사용자 정보를 가져와야 한다. 그러나 flask_jwt_extended의 get_jwt_indentity()는 @jwt_required()를 사용해야만 한다. @jwt_required()를 실행하기 위해서는 Ajax 헤더에 jwt를 담아 전송해야 한다.

 

필자가 선택한 방법은 flask_jwt_extended.decode_token()이다. 쿠키에 담긴 jwt를 decode하여 데이터를 json형식으로 리턴한다. decode_token() 함수는 @jwt_required()를 요구하지 않는다. 또한, 로그아웃 시 해당 토큰의 식별자 jti를 jwt_blocklist에 추가하여 해당 토큰의 재사용을 막는다.

코드

jwt_blocklist = set()

@app.route("/main", methods=['GET'])
def show_main():
    jwt_token = request.cookies.get('access_token')
    if jwt_token is None:
        return redirect(LOCALHOST+'/'), 400

    try:
        # token decode 후 로그아웃여부 확인 위해 jti 저장, user 정보 저장
        jti = decode_token(jwt_token)['jti']
        user_id = decode_token(jwt_token).get(IDENTITY, None)
    except ExpiredSignatureError:
        # 쿠키 시간 만료의 경우, 로그인 페이지로
        return redirect(LOCALHOST+'/'), 400

    #logout된 token의 경우 login페이지 rediect
    logoutCheck = jti in jwt_blocklist
    if logoutCheck:
        return redirect(LOCALHOST+'/'), 400


    return render_template('main.html')

# blocklist 기능 사용을 위한 세팅
@jwt.token_in_blocklist_loader
def check_if_token_is_revoked(jwt_header, jwt_payload):
    jti = jwt_payload['jti']
    return jti in jwt_blocklist


# 로그아웃 api
@app.route('/logout', methods=['GET'])
def logout_proc():
    jwt_token = request.cookies.get('access_token')

    jti = decode_token(jwt_token)['jti']
    jwt_blocklist.add(jti) # 로그인 user의 jti를 blocklist에 등록

    return jsonify({'result': 'success', 'msg': '로그아웃 성공!'})

참고

  • https://velog.io/@bongf/Flask-Flask-JWT-Extended-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-JWT-%ED%86%A0%ED%81%B0-%EC%A0%84%EC%86%A1
  • https://velog.io/@jack_lee/%EC%A0%95%EA%B8%80-WEEK00-WIL
  • https://velog.io/@insutance/JWT-%EB%9E%80#create_access_tokenidentity%EA%B0%92

 

이후

  • https://zzsza.github.io/development/2019/03/04/auth-with-flask/

프로젝트 마감 시간이 얼마 남지않아 요상한 방법으로 구현했는데, pyjwt 라이브러리를 썼으면 좀 더 손쉽게 구현했지 않았을까 하는 아쉬움이 남는다.

'IT > 에러' 카테고리의 다른 글

[에러] 백준 런타임에러(OverflowError)  (0) 2023.06.20
[에러] form input 내에서 button 또는 submit할 때, 웹페이지 refresh되는 현상  (0) 2023.06.20
[에러] 구름IDE에서 MongoDB 원격 접속 방법 및 에러  (0) 2023.06.20
[에러] case 안에서 변수 선언 in C언어  (0) 2023.06.20
'IT/에러' 카테고리의 다른 글
  • [에러] 백준 런타임에러(OverflowError)
  • [에러] form input 내에서 button 또는 submit할 때, 웹페이지 refresh되는 현상
  • [에러] 구름IDE에서 MongoDB 원격 접속 방법 및 에러
  • [에러] case 안에서 변수 선언 in C언어
KimCookieYa
KimCookieYa
무엇이 나를 살아있게 만드는가
  • KimCookieYa
    쿠키의 주저리
    KimCookieYa
  • 전체
    오늘
    어제
    • 분류 전체보기 (576)
      • 혼잣말 (88)
      • TIL (3)
      • 커리어 (24)
        • Sendy (21)
        • 외부활동 기록 (2)
      • 프로젝트 (186)
        • 티스토리 API (5)
        • 코드프레소 체험단 (89)
        • Web3 (3)
        • Pint OS (16)
        • 나만무 (14)
        • 대회 (6)
        • 정글 FE 스터디 (16)
        • MailBadara (12)
        • github.io (1)
        • 인공지능 동아리, AID (5)
        • 졸업과제 (18)
        • OSSCA 2024 (1)
      • 크래프톤 정글 2기 (80)
      • IT (169)
        • 코딩 (4)
        • CS (18)
        • 에러 (5)
        • 블록체인 (23)
        • Front-End (40)
        • 알고리즘&자료구조 정리 (3)
        • 코딩테스트 (3)
        • BOJ 문제정리 (41)
        • WILT (12)
        • ML-Agents (4)
        • 강화학습 (1)
        • Android (0)
        • LLM (2)
      • 전공 (1)
        • 머신러닝 (1)
      • 자기계발 (20)
        • 빡공단X베어유 (2)
        • 독서 (15)
  • 블로그 메뉴

    • 홈
    • 방명록
    • Github
    • Velog
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    MailBadara
    해커톤
    니어프로토콜
    크래프톤정글
    부산대
    핀토스
    알고리즘
    사이드프로젝트
    졸업과제
    OS
    글리치해커톤
    프로그래머스
    딥러닝
    자바스크립트
    센디
    numpy
    JavaScript
    리액트
    Flutter
    나만무
    RNN
    Pint OS
    머신러닝
    NEAR Protocol
    pintos
    블록체인
    docker
    react
    코드프레소
    파이썬
  • hELLO· Designed By정상우.v4.10.3
KimCookieYa
[에러] AJAX와 flask_jwt_extended로 로그인 유지하기
상단으로

티스토리툴바