| |
| """ |
| 安全相关工具函数,主要用于 JWT (JSON Web Token) 令牌的创建和验证。 |
| 使用 python-jose 库进行 JWT 操作。 |
| """ |
| from datetime import datetime, timedelta, timezone |
| from typing import Optional, Dict, Any |
| import logging |
|
|
| from jose import jwt, JWTError |
| from app import config |
|
|
| logger = logging.getLogger('my_logger') |
|
|
| |
| |
| SECRET_KEY = config.SECRET_KEY |
| |
| ALGORITHM = config.JWT_ALGORITHM |
| |
| ACCESS_TOKEN_EXPIRE_MINUTES = config.ACCESS_TOKEN_EXPIRE_MINUTES |
|
|
| def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str: |
| """ |
| 创建 JWT 访问令牌。 |
| |
| Args: |
| data (Dict[str, Any]): 需要编码到令牌 payload 中的数据字典 (例如包含用户 ID 'sub')。 |
| expires_delta (Optional[timedelta]): 可选参数,指定令牌的有效时长。 |
| 如果未提供,则使用配置中的 `ACCESS_TOKEN_EXPIRE_MINUTES`。 |
| |
| Returns: |
| str: 编码后的 JWT 访问令牌字符串。 |
| |
| Raises: |
| ValueError: 如果配置中未设置 `SECRET_KEY`。 |
| Exception: 如果在 JWT 编码过程中发生其他错误。 |
| """ |
| |
| if not SECRET_KEY: |
| logger.error("JWT SECRET_KEY 未配置,无法创建令牌。") |
| raise ValueError("JWT SECRET_KEY 未在环境变量中设置") |
|
|
| to_encode = data.copy() |
| |
| if expires_delta: |
| expire = datetime.now(timezone.utc) + expires_delta |
| else: |
| |
| expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) |
|
|
| |
| to_encode.update({"exp": expire}) |
| try: |
| |
| |
| |
| |
| encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) |
| logger.debug(f"成功创建 JWT 令牌,过期时间: {expire}") |
| return encoded_jwt |
| except Exception as e: |
| logger.error(f"创建 JWT 时发生错误: {e}", exc_info=True) |
| raise |
|
|
| def decode_access_token(token: str) -> Optional[Dict[str, Any]]: |
| """ |
| 解码并验证 JWT 访问令牌。 |
| 验证内容包括:签名、过期时间。 |
| |
| Args: |
| token (str): 需要解码和验证的 JWT 字符串。 |
| |
| Returns: |
| Optional[Dict[str, Any]]: 如果令牌有效且未过期,返回解码后的 payload 字典; |
| 否则返回 None。 |
| |
| Raises: |
| ValueError: 如果配置中未设置 `SECRET_KEY`。 |
| """ |
| |
| if not SECRET_KEY: |
| logger.error("JWT SECRET_KEY 未配置,无法解码令牌。") |
| raise ValueError("JWT SECRET_KEY 未在环境变量中设置") |
|
|
| |
| logger.debug(f"decode_access_token: 使用的 SECRET_KEY (前8位): {SECRET_KEY[:8]}...") |
| logger.debug(f"decode_access_token: 使用的 ALGORITHM: {ALGORITHM}") |
|
|
| try: |
| |
| |
| |
| |
| |
| payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) |
|
|
| |
| |
| if "exp" not in payload or not isinstance(payload["exp"], (int, float)): |
| logger.warning(f"解码后的 JWT payload 中缺少有效的 'exp' 字段: {payload}") |
| return None |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| return payload |
| except JWTError as e: |
| |
| logger.debug(f"JWT 验证失败: {e} (Token: {token[:10]}...)") |
| return None |
| except Exception as e: |
| logger.error(f"解码 token 时发生意外错误: {e}", exc_info=True) |
| return None |
|
|