mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 04:46:26 +00:00
refactor(auth): 重构会话存储逻辑,使用Redis存储完整会话数据
将原JWT载荷中内嵌的用户会话信息迁移至Redis存储,JWT仅保留会话ID,减少令牌体积并提升安全性。同步修改在线用户管理、认证依赖、租户中间件等模块适配新逻辑,新增用户会话Redis键配置,完善会话过期处理与强制下线逻辑。
This commit is contained in:
@@ -263,16 +263,23 @@ class LoginService:
|
||||
login_type=login_type,
|
||||
).model_dump_json()
|
||||
|
||||
# 会话信息存 Redis(完整 JSON),JWT sub 仅含 session_id
|
||||
await RedisCURD(redis).set(
|
||||
key=f"{RedisInitKeyConfig.USER_SESSION.key}:{session_id}",
|
||||
value=session_info,
|
||||
expire=int(refresh_expires.total_seconds()),
|
||||
)
|
||||
|
||||
access_token = create_access_token(
|
||||
payload=JWTPayloadSchema(
|
||||
sub=session_info,
|
||||
sub=session_id,
|
||||
is_refresh=False,
|
||||
exp=now + access_expires,
|
||||
)
|
||||
)
|
||||
refresh_token = create_access_token(
|
||||
payload=JWTPayloadSchema(
|
||||
sub=session_info,
|
||||
sub=session_id,
|
||||
is_refresh=True,
|
||||
exp=now + refresh_expires,
|
||||
)
|
||||
@@ -309,9 +316,14 @@ class LoginService:
|
||||
if not token_payload.is_refresh:
|
||||
raise CustomException(msg="非法凭证,请传入刷新令牌")
|
||||
|
||||
session_info = json.loads(token_payload.sub)
|
||||
session_id = session_info.get("session_id")
|
||||
user_id = session_info.get("user_id")
|
||||
session_id = token_payload.sub
|
||||
session_info = await RedisCURD(redis).get(
|
||||
f"{RedisInitKeyConfig.USER_SESSION.key}:{session_id}"
|
||||
)
|
||||
if not session_info:
|
||||
raise CustomException(msg="会话已过期,请重新登录")
|
||||
|
||||
user_id = json.loads(session_info).get("user_id")
|
||||
|
||||
if not session_id or not user_id:
|
||||
raise CustomException(msg="非法凭证,无法获取会话编号或用户ID")
|
||||
@@ -327,11 +339,15 @@ class LoginService:
|
||||
refresh_expires = timedelta(seconds=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
|
||||
now = datetime.now()
|
||||
|
||||
session_info_json = session_info if isinstance(session_info, str) else json.dumps(session_info)
|
||||
# 延长会话信息 Redis TTL
|
||||
await RedisCURD(redis).expire(
|
||||
key=f"{RedisInitKeyConfig.USER_SESSION.key}:{session_id}",
|
||||
expire=int(refresh_expires.total_seconds()),
|
||||
)
|
||||
|
||||
access_token = create_access_token(
|
||||
payload=JWTPayloadSchema(
|
||||
sub=session_info_json,
|
||||
sub=session_id,
|
||||
is_refresh=False,
|
||||
exp=now + access_expires,
|
||||
)
|
||||
@@ -339,7 +355,7 @@ class LoginService:
|
||||
|
||||
refresh_token_new = create_access_token(
|
||||
payload=JWTPayloadSchema(
|
||||
sub=session_info_json,
|
||||
sub=session_id,
|
||||
is_refresh=True,
|
||||
exp=now + refresh_expires,
|
||||
)
|
||||
@@ -368,14 +384,14 @@ class LoginService:
|
||||
async def logout(redis: Redis, token: LogoutPayloadSchema) -> bool:
|
||||
"""退出登录"""
|
||||
payload: JWTPayloadSchema = decode_access_token(token=token.token)
|
||||
session_info = json.loads(payload.sub)
|
||||
session_id = session_info.get("session_id")
|
||||
session_id = payload.sub
|
||||
|
||||
if not session_id:
|
||||
raise CustomException(msg="非法凭证,无法获取会话编号")
|
||||
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}")
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:{session_id}")
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.USER_SESSION.key}:{session_id}")
|
||||
|
||||
logger.info(f"用户退出登录成功,会话编号:{session_id}")
|
||||
|
||||
@@ -454,33 +470,38 @@ class LoginService:
|
||||
if not session_id or not session_info:
|
||||
raise CustomException(msg="会话已失效")
|
||||
|
||||
# 更新会话中的租户 ID 并写回 Redis
|
||||
session_info["tenant_id"] = tenant_id
|
||||
|
||||
refresh_expires = timedelta(seconds=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
|
||||
from app.core.redis_crud import RedisCURD
|
||||
from app.core.security import create_access_token
|
||||
|
||||
await RedisCURD(redis).set(
|
||||
key=f"{RedisInitKeyConfig.USER_SESSION.key}:{session_id}",
|
||||
value=json.dumps(session_info) if isinstance(session_info, dict) else session_info,
|
||||
expire=int(refresh_expires.total_seconds()),
|
||||
)
|
||||
|
||||
access_expires = timedelta(seconds=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
now = datetime.now()
|
||||
|
||||
new_access_token = create_access_token(
|
||||
payload=JWTPayloadSchema(
|
||||
sub=json.dumps(session_info),
|
||||
sub=session_id,
|
||||
is_refresh=False,
|
||||
exp=now + access_expires,
|
||||
)
|
||||
)
|
||||
|
||||
from app.core.redis_crud import RedisCURD
|
||||
|
||||
await RedisCURD(redis).set(
|
||||
key=f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}",
|
||||
value=new_access_token,
|
||||
expire=int(access_expires.total_seconds()),
|
||||
)
|
||||
|
||||
refresh_expires = timedelta(seconds=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
|
||||
new_refresh_token = create_access_token(
|
||||
payload=JWTPayloadSchema(
|
||||
sub=json.dumps(session_info),
|
||||
sub=session_id,
|
||||
is_refresh=True,
|
||||
exp=now + refresh_expires,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user