扫码登录,获取cookies

This commit is contained in:
2026-03-09 16:10:29 +08:00
parent 754e720ba7
commit 8229208165
7775 changed files with 1150053 additions and 208 deletions

View File

@@ -7,9 +7,7 @@ import hashlib
import jwt
import secrets
from datetime import datetime, timedelta
from typing import Optional
import redis.asyncio as aioredis
from typing import Optional, Dict
from shared.config import shared_settings
@@ -17,17 +15,28 @@ from shared.config import shared_settings
BCRYPT_ROUNDS = 12
REFRESH_TOKEN_TTL = 7 * 24 * 3600 # 7 days in seconds
# Lazy-initialised async Redis client
_redis_client: Optional[aioredis.Redis] = None
# Lazy-initialised async Redis client (可选)
_redis_client: Optional[object] = None
# 内存存储(本地开发用,不使用 Redis 时)
_memory_store: Dict[str, tuple[str, datetime]] = {}
async def get_redis() -> aioredis.Redis:
"""Return a shared async Redis connection."""
async def get_redis():
"""Return a shared async Redis connection if enabled."""
if not shared_settings.USE_REDIS:
return None
global _redis_client
if _redis_client is None:
_redis_client = aioredis.from_url(
shared_settings.REDIS_URL, decode_responses=True
)
try:
import redis.asyncio as aioredis
_redis_client = aioredis.from_url(
shared_settings.REDIS_URL, decode_responses=True
)
except Exception as e:
print(f"[警告] Redis 连接失败: {e},将使用内存存储")
return None
return _redis_client
def hash_password(password: str) -> str:
@@ -118,31 +127,68 @@ def _hash_token(token: str) -> str:
return hashlib.sha256(token.encode("utf-8")).hexdigest()
def _clean_expired_tokens():
"""清理过期的内存 token"""
now = datetime.utcnow()
expired_keys = [k for k, (_, exp) in _memory_store.items() if exp < now]
for k in expired_keys:
del _memory_store[k]
async def create_refresh_token(user_id: str) -> str:
"""
Generate a cryptographically random refresh token, store its hash in Redis
with a 7-day TTL, and return the raw token string.
(or memory if Redis disabled) with a 7-day TTL, and return the raw token string.
"""
token = secrets.token_urlsafe(48)
token_hash = _hash_token(token)
r = await get_redis()
await r.setex(f"refresh_token:{token_hash}", REFRESH_TOKEN_TTL, user_id)
if r:
# 使用 Redis
await r.setex(f"refresh_token:{token_hash}", REFRESH_TOKEN_TTL, user_id)
else:
# 使用内存存储
_clean_expired_tokens()
expire_at = datetime.utcnow() + timedelta(seconds=REFRESH_TOKEN_TTL)
_memory_store[f"refresh_token:{token_hash}"] = (user_id, expire_at)
return token
async def verify_refresh_token(token: str) -> Optional[str]:
"""
Verify a refresh token by looking up its hash in Redis.
Verify a refresh token by looking up its hash in Redis or memory.
Returns the associated user_id if valid, None otherwise.
"""
token_hash = _hash_token(token)
key = f"refresh_token:{token_hash}"
r = await get_redis()
user_id = await r.get(f"refresh_token:{token_hash}")
return user_id
if r:
# 使用 Redis
user_id = await r.get(key)
return user_id
else:
# 使用内存存储
_clean_expired_tokens()
if key in _memory_store:
user_id, expire_at = _memory_store[key]
if expire_at > datetime.utcnow():
return user_id
return None
async def revoke_refresh_token(token: str) -> None:
"""Delete a refresh token from Redis (used during rotation)."""
"""Delete a refresh token from Redis or memory (used during rotation)."""
token_hash = _hash_token(token)
key = f"refresh_token:{token_hash}"
r = await get_redis()
await r.delete(f"refresh_token:{token_hash}")
if r:
# 使用 Redis
await r.delete(key)
else:
# 使用内存存储
if key in _memory_store:
del _memory_store[key]