Krafton_jungle 5기 2일차 TIL - jwt
난 JWT 토큰이 밉다.
현제 진행 상황을 얘기하자면 로그인 인증 로직을 제외한 거의 모든 기능을 구현한 상태다.
하지만 그렇게 localStorage에 저장된 jwt 토큰값을 가져와 일일히 header에 담아주려면 너무 번거로운 뿐더러 헤더에 담기 위해 ajax를 사용하면 render_template으로 view가 렌더링 되지 않고 쌩 HTML 코드가 리턴되는 대참사가 발생해버린다.
정확한 이유는 아직 찾아보지 못했지만 아마 SSR 내부에 렌더링을 해주는 로직과 뭔가가 충돌이 일어나는 것 같다.
처음엔 인증된 사용자만 볼수 있는 View를 반환하려면 뷰를 랜더링 해주는 router, 인증을 수행해주는 ajax 이런식으로 개발을 진행해왔는데 이건 정말 불필요한 노동에 불과하다는 생각이 들어 다른 방법을 알아보았다.
처음 알아봤던 방법은 jwt_extended에서 제공하는 jwt_required 데코레이터.
하지만 이 방법은 쿠키에 저장된 값은 불러오지 않았고 결국 뷰가 전환되는 과정에서 인증 절차를 수행하지 못했다.
그렇게 머리를 싸매서 나온 결과가 내가 직접 쿠키에서 JWT 토큰을 decode 하는 데코레이터를 작성해보자 였다.
로직은 간단하다.
Router가 작동하기 전에 쿠키에서 토큰을 조회하고 이를 decode 한다.
이 과정에서 exception이 발생하면 errorpage를 리턴해주고, 그게 아니라면 해당 Router의 매개변수로 decode된 토큰의 payload를 넣어준다.
데코레이터 코드
def require_access_token(func):
@wraps(func)
def wrapper(*args, **kwargs):
access_token = request.cookies.get("access_token_cookie")
if access_token:
try:
decode_token = jwt.decode(access_token, SECRET_KEY, algorithms=['HS256'])
return func(decode_token["sub"], *args, **kwargs) # 디코딩된 토큰을 함수에 전달하고 결과를 반환
except jwt.ExpiredSignatureError:
# 토큰 만료 에러 처리
raise ValueError("토큰이 만료되었습니다. 다시 로그인해주세요")
except jwt.InvalidTokenError:
# 유효하지 않은 토큰 에러 처리
raise ValueError("유효하지 않은 로그인. 다시 로그인해주세요.")
else:
# 토큰이 없는 경우 에러를 던집니다.
raise ValueError("로그인 정보가 존재하지 않습니다.")
return wrapper
보다싶이 jwt 토큰을 decode하고 그 과정에서 에러가 발생하면 ValueError를 발생시키고 있다.
에러 핸들러
@app.errorhandler(ValueError)
def handle_value_error(error):
# 에러를 받아서 에러 페이지를 렌더링합니다.
return render_template('error.html', error=error), 400
에러가 발생하면 에러 메세지와 error 페이지를 렌더링 해주는 코드.
마지막으로 사용 예시
@bp.route('/list')
@require_access_token
def list(decode_token):
page = request.args.get('page', 1, type=int)
users = db.jungle.find({}, {"_id": 0, "user_pw": 0}).skip((page-1) * PAGE_SIZE).limit(PAGE_SIZE)
total_count = db.jungle.count_documents({})
last_page_num = math.ceil(total_count / PAGE_SIZE)
user_list = [user for user in users]
data = {"user_list" : user_list, "last_page_num" : last_page_num}
return render_template('list.html', data=data)
지금은 decode_token을 사용하고 있지 않지만 mypage를 조회한다던가 로그인 한 유저의 식별값이 필요한 경우는 유용하게 쓸 수 있다.
앓던 이가 빠진 기분이니 어서 프로젝트 완성시켜야지