Files
2026-09-12 19:09:28 +08:00

103 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
认证与权限配置模块
=============================================================
包含 JWT 令牌、权限验证、登录白名单相关的所有配置项。
依赖:config/__init__.py 中的 _load_env_file() 已提前加载 .env
"""
import os
import logging
logger = logging.getLogger(__name__)
# ============================================================
# JWT令牌配置
# ============================================================
# JWT密钥(用于令牌签名和验证,必须保密)
# 从环境变量读取,未配置则拒绝启动,防止使用已知弱密钥
JWT_SALT = os.getenv('JWT_SALT', '')
# 滑动会话空闲超时(分钟):用户在此时间内无任何请求,会话过期需重新登录。
# 每收到一次有效请求,Redis 会话 TTL 被重置为该值(sliding session)。
JWT_EXPIRE_MINUTES = int(os.getenv('JWT_EXPIRE_MINUTES', 20))
# JWT 令牌绝对有效期(分钟):即使一直活跃,令牌最长有效时间,到期强制重新登录。
# 防止会话无限续期,需大于 JWT_EXPIRE_MINUTES(默认 1440 = 24小时)。
JWT_ABSOLUTE_TIMEOUT_MINUTES = int(os.getenv('JWT_ABSOLUTE_TIMEOUT_MINUTES', 1440))
# 启动校验:绝对有效期须 ≥ 空闲超时,否则滑动会话模型失效(一直活跃也会被绝对期限打断)
if JWT_ABSOLUTE_TIMEOUT_MINUTES < JWT_EXPIRE_MINUTES:
raise RuntimeError(
f"JWT_ABSOLUTE_TIMEOUT_MINUTES({JWT_ABSOLUTE_TIMEOUT_MINUTES}) "
f"不能小于 JWT_EXPIRE_MINUTES({JWT_EXPIRE_MINUTES}),"
"绝对有效期须大于等于滑动会话空闲超时"
)
# 登录失败次数上限(同一IP+用户名连续失败超过此次数后锁定)
LOGIN_FAIL_MAX = int(os.getenv('LOGIN_FAIL_MAX', 5))
# 登录锁定时间(秒)(锁定期间禁止该IP+用户名登录)
LOGIN_LOCK_SECONDS = int(os.getenv('LOGIN_LOCK_SECONDS', 300))
# ============================================================
# 权限验证配置
# ============================================================
# 是否启用权限验证:True-启用(需要登录验证),False-禁用(所有请求放行)
PERMISSION_ENABLED = os.getenv('PERMISSION_ENABLED', 'True') == 'True'
# 系统内部调用的API密钥(用于定时任务等无用户场景)
# 调用方需在请求头中携带 X-Internal-API-Key 或 X-Scheduler-Key
INTERNAL_API_KEY = os.getenv('INTERNAL_API_KEY', '')
# 启动时校验:该密钥是权限全绕过的凭据,必须为强随机值,不符合则 fail-closed 拒绝启动。
# 规则化校验(长度 ≥32)而非黑名单枚举,任何弱值/新弱值都会被拦截。
if not INTERNAL_API_KEY:
raise RuntimeError(
"INTERNAL_API_KEY 未配置!请在 .env 中设置内部调用密钥。\n"
"生成方式:python -c \"import secrets; print(secrets.token_urlsafe(32))\""
)
if len(INTERNAL_API_KEY) < 32:
raise RuntimeError(
f"INTERNAL_API_KEY 长度不足 32 位(当前 {len(INTERNAL_API_KEY)} 位),"
"拒绝启动(fail-closed)。请更换为强随机密钥:\n"
"python -c \"import secrets; print(secrets.token_urlsafe(32))\""
)
# 内部调用白名单IP(逗号分隔,支持通配符如 127.0.0.*)
# 在此列表中的IP发起的请求将跳过权限验证
INTERNAL_ALLOWED_IPS = os.getenv('INTERNAL_ALLOWED_IPS', '127.0.0.1')
# ============================================================
# 登录白名单配置(支持通配符)
# ============================================================
# 精确匹配白名单(逗号分隔,完全匹配路径才放行)
# 支持路径:/login、/captcha、/favicon.ico
LOGIN_WHITELIST_EXACT = os.getenv('LOGIN_WHITELIST_EXACT', '')
# 前缀匹配白名单(逗号分隔,路径以此开头即放行)
# 支持路径:/job/sendSms、/public、/static、/api/webhook
LOGIN_WHITELIST_PREFIX = os.getenv('LOGIN_WHITELIST_PREFIX', '')
# 通配符匹配白名单(逗号分隔,支持 * 和 ? 通配符)
# * 匹配任意多个字符,? 匹配单个字符
# 示例:/job/*/send* 可匹配 /job/1/sendSms、/job/2/sendEmail
LOGIN_WHITELIST_WILDCARD = os.getenv('LOGIN_WHITELIST_WILDCARD', '')
# 是否启用白名单调试日志:True-打印匹配日志,False-不打印
LOGIN_WHITELIST_DEBUG = os.getenv('LOGIN_WHITELIST_DEBUG', 'False') == 'True'
# 是否在白名单模式下记录访问日志:True-记录,False-不记录
LOGIN_WHITELIST_LOG_ACCESS = os.getenv('LOGIN_WHITELIST_LOG_ACCESS', 'False') == 'True'
# ============================================================
# 万能验证码(仅 DEMO 模式)
# ============================================================
# 万能验证码(仅在 FLASK_DEMO=True 时生效,生产环境必须为空)
CAPTCHA_BYPASS_CODE = os.getenv('CAPTCHA_BYPASS_CODE', '')