토큰 로직 수정
parent
97a6384a54
commit
2c8c16c288
|
|
@ -1,12 +1,13 @@
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
|
from fastapi import HTTPException
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||||
from sqlalchemy.orm import DeclarativeBase
|
from sqlalchemy.orm import DeclarativeBase
|
||||||
|
|
||||||
from app.utils.logger import get_logger
|
from app.utils.logger import get_logger
|
||||||
from config import db_settings
|
from config import db_settings
|
||||||
import traceback
|
|
||||||
|
|
||||||
logger = get_logger("database")
|
logger = get_logger("database")
|
||||||
|
|
||||||
|
|
@ -134,15 +135,16 @@ async def get_session() -> AsyncGenerator[AsyncSession, None]:
|
||||||
# )
|
# )
|
||||||
try:
|
try:
|
||||||
yield session
|
yield session
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
logger.error(
|
logger.error(
|
||||||
f"[get_session] ROLLBACK - error: {type(e).__name__}: {e}, "
|
f"[get_session] ROLLBACK - error: {type(e).__name__}: {e}, "
|
||||||
f"duration: {(time.perf_counter() - start_time)*1000:.1f}ms"
|
f"duration: {(time.perf_counter() - start_time)*1000:.1f}ms"
|
||||||
)
|
)
|
||||||
raise e
|
raise
|
||||||
finally:
|
finally:
|
||||||
total_time = time.perf_counter() - start_time
|
total_time = time.perf_counter() - start_time
|
||||||
# logger.debug(
|
# logger.debug(
|
||||||
|
|
@ -170,6 +172,8 @@ async def get_background_session() -> AsyncGenerator[AsyncSession, None]:
|
||||||
# )
|
# )
|
||||||
try:
|
try:
|
||||||
yield session
|
yield session
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|
@ -178,7 +182,7 @@ async def get_background_session() -> AsyncGenerator[AsyncSession, None]:
|
||||||
f"duration: {(time.perf_counter() - start_time)*1000:.1f}ms"
|
f"duration: {(time.perf_counter() - start_time)*1000:.1f}ms"
|
||||||
)
|
)
|
||||||
logger.debug(traceback.format_exc())
|
logger.debug(traceback.format_exc())
|
||||||
raise e
|
raise
|
||||||
finally:
|
finally:
|
||||||
total_time = time.perf_counter() - start_time
|
total_time = time.perf_counter() - start_time
|
||||||
# logger.debug(
|
# logger.debug(
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ class SocialAccountService:
|
||||||
else:
|
else:
|
||||||
# DB datetime은 naive, now()는 aware이므로 naive로 통일하여 비교
|
# DB datetime은 naive, now()는 aware이므로 naive로 통일하여 비교
|
||||||
current_time = now().replace(tzinfo=None)
|
current_time = now().replace(tzinfo=None)
|
||||||
buffer_time = current_time + timedelta(hours=1)
|
buffer_time = current_time + timedelta(minutes=20)
|
||||||
if account.token_expires_at <= buffer_time:
|
if account.token_expires_at <= buffer_time:
|
||||||
should_refresh = True
|
should_refresh = True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,11 @@ from app.user.services.auth import (
|
||||||
AdminRequiredError,
|
AdminRequiredError,
|
||||||
InvalidTokenError,
|
InvalidTokenError,
|
||||||
MissingTokenError,
|
MissingTokenError,
|
||||||
|
TokenExpiredError,
|
||||||
UserInactiveError,
|
UserInactiveError,
|
||||||
UserNotFoundError,
|
UserNotFoundError,
|
||||||
)
|
)
|
||||||
from app.user.services.jwt import decode_token
|
from app.user.services.jwt import decode_token, is_token_expired
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -58,6 +59,9 @@ async def get_current_user(
|
||||||
|
|
||||||
payload = decode_token(token)
|
payload = decode_token(token)
|
||||||
if payload is None:
|
if payload is None:
|
||||||
|
if is_token_expired(token):
|
||||||
|
logger.info(f"[AUTH-DEP] Access Token 만료 - token: ...{token[-20:]}")
|
||||||
|
raise TokenExpiredError()
|
||||||
logger.warning(f"[AUTH-DEP] Access Token 디코딩 실패 - token: ...{token[-20:]}")
|
logger.warning(f"[AUTH-DEP] Access Token 디코딩 실패 - token: ...{token[-20:]}")
|
||||||
raise InvalidTokenError()
|
raise InvalidTokenError()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ from app.user.services.jwt import (
|
||||||
get_access_token_expire_seconds,
|
get_access_token_expire_seconds,
|
||||||
get_refresh_token_expires_at,
|
get_refresh_token_expires_at,
|
||||||
get_token_hash,
|
get_token_hash,
|
||||||
|
is_token_expired,
|
||||||
)
|
)
|
||||||
from app.user.services.kakao import kakao_client
|
from app.user.services.kakao import kakao_client
|
||||||
|
|
||||||
|
|
@ -212,6 +213,9 @@ class AuthService:
|
||||||
# 1. 토큰 디코딩 및 검증
|
# 1. 토큰 디코딩 및 검증
|
||||||
payload = decode_token(refresh_token)
|
payload = decode_token(refresh_token)
|
||||||
if payload is None:
|
if payload is None:
|
||||||
|
if is_token_expired(refresh_token):
|
||||||
|
logger.info(f"[AUTH] 토큰 갱신 실패 [1/8 만료] - token: ...{refresh_token[-20:]}")
|
||||||
|
raise TokenExpiredError()
|
||||||
logger.warning(f"[AUTH] 토큰 갱신 실패 [1/8 디코딩] - token: ...{refresh_token[-20:]}")
|
logger.warning(f"[AUTH] 토큰 갱신 실패 [1/8 디코딩] - token: ...{refresh_token[-20:]}")
|
||||||
raise InvalidTokenError()
|
raise InvalidTokenError()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,28 @@ def decode_token(token: str) -> Optional[dict]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_token_expired(token: str) -> bool:
|
||||||
|
"""
|
||||||
|
토큰이 만료됐는지 확인 (서명/형식은 유효하지만 exp 초과인 경우)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True: 서명은 유효하나 만료된 토큰, False: 형식/서명 자체가 잘못된 토큰
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(
|
||||||
|
token,
|
||||||
|
jwt_settings.JWT_SECRET,
|
||||||
|
algorithms=[jwt_settings.JWT_ALGORITHM],
|
||||||
|
options={"verify_exp": False},
|
||||||
|
)
|
||||||
|
exp = payload.get("exp")
|
||||||
|
if exp is None:
|
||||||
|
return False
|
||||||
|
return datetime.fromtimestamp(exp) < datetime.now()
|
||||||
|
except JWTError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_token_hash(token: str) -> str:
|
def get_token_hash(token: str) -> str:
|
||||||
"""
|
"""
|
||||||
토큰의 SHA-256 해시값 생성
|
토큰의 SHA-256 해시값 생성
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue