Python 언어 - Flask / flask_jwt_extended 사용
항해 토이 프로젝트에 로그인 관련해서 매니저님이 언급해주신 JWT 토큰에 대해 구현 코드를 뜯어보고 공부해보자.
JWT란?
JSON포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token이다. Token 자체를 정보로 사용하는 Self-Contained 방식으로 정보를 안전하게 전달.
* 클레임(Claim)은 주체가 수행할 수 있는 작업보다는 주체가 무엇인지를 표현하는 이름과 값의 쌍을 말한다.
예를 들어 신분증의 생년월일 이란 이름이 클레임의 이름이 되며 1970년 1월 1일이라는 값은 클레임의 값이 된다.
구조, 작동원리 및 장점 단점은 아래 포스팅이 잘 설명되어있다.
https://blog.naver.com/PostView.naver?blogId=dktmrorl&logNo=222410946965
+ jwt 토큰은 만료를 정해주지 않으면 그대로 남기 때문에 만료와 갱신에 신경써야한다
Access_token 의 기간이 만료되면 Refresh_token을 가지고 Access_token을 재발급 받아야 한다.
그래서 토큰 시간이 만료되기 전에 프런트에서 refresh 요청을 보낸다
구현코드(BE)
임포트 내용
from flask import Flask, request, jsonify, render_template
from flask_jwt_extended import (
JWTManager, jwt_required, create_access_token, get_jwt_identity, unset_jwt_cookies, create_refresh_token,
set_access_cookies, set_refresh_cookies, get_jwt)
# refresh 갱신 관련해 처리하려면 아래사항도 임포트
from datetime import timedelta, datetime, timezone
초반 세팅
#----------------------------------------------------------------------
# __name__ 변수는 모듈의 이름을 가진 변수로 실행하는 기준이 되는 .py 파일의 이름은 __main__에 해당
# 만약 app.py에서 another.py라는 모듈을 사용했다면 app.py의 __name__ == __main__ ,
# another.py의 __name__ == another가 된다.
# Flask(__name__) 라우팅 경로를 설정. 해당 라우팅 경로로 요청이 올 때 실행할 함수를 아래에 작성한다
app = Flask(__name__)
# jwt 가장 기초세팅 start ------------------------------------------------------------------------
app.config["JWT_COOKIE_SECURE"] = False # https를 통해서만 cookie가 갈수 있는지
app.config["JWT_TOKEN_LOCATION"] = ["cookies"] # 토큰을 어디서 찾을지 설정
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1) # 토큰 만료시간 설정 기본은 30분
app.config["JWT_SECRET_KEY"] = "직접 정의" # 토큰 암호화에 이용되는 히든키로 직접 정의
jwt = JWTManager(app)
# jwt 가장 기초세팅 end ------------------------------------------------------------------------
로그인
@app.route("/login", methods=["POST"])
def login():
user = request.get_json()
username = user['username']
password = user['password']
# 여기서 DB단 비교 부분이 들어가야함
if username != "test" or password != "test":
return jsonify({"msg": "Bad username or password"}), 401 # 접근불가 오류
access_token = create_access_token(identity=username) # jwt token 생성
refresh_token = create_refresh_token(identity=username) # 갱신을 위한 refresh_token 생성
# 이걸 리턴해서 어떻게 쓰는거지? > 쿠키에 저장한다
response = jsonify({"login" : True})
# response을 만든이유가 단지 메세지를 위한게 아니라 쿠키를 같이 보내야하기 때문에 만든듯
# 쿠키에 토큰을 넣으면서 response 안에 jwt cookie를 넣어 리턴하려고
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
return response, 200 # 서버가 제대로 요청을 처리했다는 성공
유효성 검사
@app.route("/protected", methods=["GET"])
@jwt_required() # 토큰이 인정된 (접근권한이 인정된) 유저만이 이 API를 사용할 수 있다. 유효성 테스트
def protected():
current_user = get_jwt_identity() # token으로부터 저장된 데이터를 불러온다
return jsonify(logged_in_as=current_user), 200
갱신
@app.after_request
def refresh_expiring_jwts(response):
try:
exp_timestamp = get_jwt()["exp"] # 만료시간을 찍는
now = datetime.now(timezone.utc) # 현재시간 출력하는데, 기준시는 협정 세계시
target_timestamp = datetime.timestamp(now + timedelta(minutes=30)) # 지금 시간으로 부터 30분 추가한 대상시
if target_timestamp > exp_timestamp: # 대상시가 만료시간보다 크면
access_token = create_access_token(identity=get_jwt_identity()) # 다시 토큰을 갱신
set_access_cookies(response, access_token) # 갱신한 토큰을 다시 쿠키에 저장
return response # 갱신한 토큰이 담긴 쿠키가 있는 response를 반환
except (RuntimeError, KeyError):
return response # 유효하지 않은 jwt라 갱신하지 않고 오리지날을 반환
로그아웃
@app.route("/logout", methods=["GET"])
def logout():
response = jsonify({"msg": "Logout Successful"})
unset_jwt_cookies(response) # 쿠키 없애는
return response
구현코드(FE)
로그인 : 꼭 JSON형태로 보낼필요는 없지만 서버단에서 json으로 받도록 코딩해놔서 JSON으로 전달
function login() {
var data = {
"username": $('#username').val(),
"password": $('#password').val()
}
$.ajax({
type: "POST",
url: "/login",
data: JSON.stringify(data),
dataType: "json",
success: function (response) {
alert(response["login"]);
}
});
유효성 검사 사용할때는 서버단에 @jwt_required()가 있으면 된다.
function writeform(){
$.ajax({
type: "GET",
url: "/protected",
data: {},
success: function (response) {
console.log(response)
}
});
}
JWT에 대한 이해:
https://flask-jwt-extended.readthedocs.io/en/stable/
https://coding-hyeok.tistory.com/m/86
'Programming > Python' 카테고리의 다른 글
Python] flask 동적 URL 구현 코드 (1) | 2022.10.31 |
---|---|
Python] 유튜브 크롤링 구현 (Selenium / 셀레늄) (0) | 2022.10.25 |
Python] 기본 환경 설정 - Django 설치 (0) | 2022.08.31 |
Python pip install 또는 uninstall 방법 (0) | 2022.08.31 |
PyCharm 단축키 모음 (0) | 2022.08.31 |