103 lines
4.7 KiB
Python
103 lines
4.7 KiB
Python
"""
|
||
认证与权限配置模块
|
||
=============================================================
|
||
|
||
包含 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', '')
|