Skip to content

12-配置管理

Python 3.11+ | Flask 3.x

本章讲解 Flask 配置管理的完整体系:从基础用法到多环境分离,再到生产级安全实践。


第一部分:配置基础(L1)

1.1 实际场景

你的 Flask 应用需要管理数据库连接、密钥、调试开关等参数。硬编码在代码中会导致环境切换困难、密钥泄露等问题。

问题:Flask 如何统一管理应用配置?

1.2 app.config 是什么

app.config 是 Flask 应用的核心配置容器,本质是一个继承自 dict 的类字典对象,但具有额外功能:

app.config 结构:
┌─────────────────────────────────────────────────────────────┐
│                    Flask Config 对象                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  • 继承自 dict,支持所有字典操作                              │
│  • 自动加载 Flask 内置默认配置                                │
│  • 支持多种加载方式:文件、对象、环境变量、JSON               │
│  • 配置键约定:全大写字母(如 SECRET_KEY、DEBUG)             │
│                                                             │
│  工作流程:                                                   │
│  1. 创建 app 时 → 加载内置默认配置                           │
│  2. 调用 from_object → 从 Python 对象加载                    │
│  3. 调用 from_envvar → 从环境变量加载                        │
│  4. 运行时可直接修改 → app.config['KEY'] = value             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.3 基本用法:直接赋值

python
# app_simple.py
from flask import Flask

app: Flask = Flask(__name__)

# 直接设置配置项
app.config["DEBUG"] = True
app.config["SECRET_KEY"] = "dev-secret-key-change-in-production"
app.config["DATABASE_URI"] = "sqlite:///app.db"
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024  # 16MB

# 读取配置
secret: str = app.config["SECRET_KEY"]
debug_mode: bool = app.config.get("DEBUG", False)
代码片段含义说明
app.config["KEY"] = value设置配置项键名约定全大写
app.config["KEY"]获取配置项不存在时抛 KeyError
app.config.get("KEY", default)安全获取不存在时返回默认值

注意:直接赋值适合简单场景,但生产应用推荐使用配置对象或配置文件。

1.4 从 Python 对象加载:from_object

from_object 是 Flask 最常用的配置加载方式,从 Python 模块或类中加载所有大写字母开头的属性:

python
# config.py
class Config:
    """基础配置类"""

    SECRET_KEY: str = "default-secret-key"
    DEBUG: bool = False
    TESTING: bool = False
    DATABASE_URI: str = "sqlite:///app.db"
    MAX_CONTENT_LENGTH: int = 16 * 1024 * 1024


class DevelopmentConfig(Config):
    """开发环境配置"""

    DEBUG: bool = True
    DATABASE_URI: str = "sqlite:///dev.db"


class ProductionConfig(Config):
    """生产环境配置"""

    SECRET_KEY: str = "production-secret-key-from-env"
    DATABASE_URI: str = "postgresql://user:pass@db-server/prod_db"
python
# app_from_object.py
from flask import Flask
from config import DevelopmentConfig

app: Flask = Flask(__name__)

# 从类加载配置(只加载大写字母开头的属性)
app.config.from_object(DevelopmentConfig)

# 验证加载结果
print(app.config["DEBUG"])        # True
print(app.config["DATABASE_URI"]) # sqlite:///dev.db
代码片段含义
from_object(DevelopmentConfig)从类加载,继承链上的属性也会加载
from_object("config.ProductionConfig")从字符串路径加载,支持动态导入
from_object(config_module)从模块对象加载,加载模块内所有大写属性

1.5 从环境变量加载:from_envvar

python
# app_from_envvar.py
import os
from flask import Flask

app: Flask = Flask(__name__)

# 方式一:指定环境变量名,该变量值为配置文件路径
# 假设环境变量 FLASK_CONFIG_FILE=/etc/myapp/config.py
app.config.from_envvar("FLASK_CONFIG_FILE")

# 方式二:设置 silent=True,环境变量不存在时不报错
app.config.from_envvar("FLASK_CONFIG_FILE", silent=True)

# 方式三:结合默认配置
app.config.from_object("config.DefaultConfig")
app.config.from_envvar("FLASK_CONFIG_FILE", silent=True)  # 覆盖默认配置
bash
# 终端设置环境变量
export FLASK_CONFIG_FILE=/path/to/config.py
flask run

第二部分:多环境配置(L1)

2.1 开发/测试/生产环境分离

不同环境需要不同的配置参数:

环境配置对比:
┌─────────────────┬──────────────────┬──────────────────┬──────────────────┐
│     配置项       │    开发环境       │    测试环境       │    生产环境       │
├─────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ DEBUG           │ True             │ False            │ False            │
│ TESTING         │ False            │ True             │ False            │
│ DATABASE_URI    │ sqlite:///dev.db │ sqlite:///test.db│ postgresql://... │
│ SECRET_KEY      │ dev-key          │ test-key         │ 加密存储的密钥    │
│ LOG_LEVEL       │ DEBUG            │ INFO             │ WARNING          │
│ MAIL_SERVER     │ localhost        │ localhost        │ smtp.company.com │
│ CACHE_TYPE      │ SimpleCache      │ SimpleCache      │ RedisCache       │
└─────────────────┴──────────────────┴──────────────────┴──────────────────┘

2.2 配置类模式

python
# config.py
import os


class Config:
    """基础配置——所有环境共有的配置"""

    # 安全配置
    SECRET_KEY: str = os.environ.get("SECRET_KEY", "dev-secret-key")

    # 数据库
    SQLALCHEMY_DATABASE_URI: str = os.environ.get(
        "DATABASE_URL", "sqlite:///app.db"
    )
    SQLALCHEMY_TRACK_MODIFICATIONS: bool = False

    # 文件上传
    MAX_CONTENT_LENGTH: int = 16 * 1024 * 1024  # 16MB
    UPLOAD_FOLDER: str = os.path.join(os.path.dirname(__file__), "uploads")

    # 分页
    ITEMS_PER_PAGE: int = 20

    # 缓存
    CACHE_TYPE: str = "SimpleCache"
    CACHE_DEFAULT_TIMEOUT: int = 300

    @staticmethod
    def init_app(app) -> None:
        """每个环境共享的初始化逻辑"""
        pass


class DevelopmentConfig(Config):
    """开发环境"""

    DEBUG: bool = True
    TESTING: bool = False
    SQLALCHEMY_DATABASE_URI: str = os.environ.get(
        "DEV_DATABASE_URL", "sqlite:///dev.db"
    )
    CACHE_TYPE: str = "SimpleCache"
    MAIL_SERVER: str = "localhost"
    MAIL_PORT: int = 25
    MAIL_USE_TLS: bool = False


class TestingConfig(Config):
    """测试环境"""

    DEBUG: bool = False
    TESTING: bool = True
    SQLALCHEMY_DATABASE_URI: str = os.environ.get(
        "TEST_DATABASE_URL", "sqlite:///test.db"
    )
    WTF_CSRF_ENABLED: bool = False  # 测试时禁用 CSRF


class ProductionConfig(Config):
    """生产环境"""

    DEBUG: bool = False
    TESTING: bool = False
    SQLALCHEMY_DATABASE_URI: str = os.environ.get("DATABASE_URL", "")
    CACHE_TYPE: str = "RedisCache"
    CACHE_REDIS_URL: str = os.environ.get("REDIS_URL", "redis://localhost:6379")

    @staticmethod
    def init_app(app) -> None:
        """生产环境额外初始化"""
        Config.init_app(app)
        # 可在此添加日志配置、错误监控等


# 配置映射字典,方便动态选择
config_map: dict[str, type[Config]] = {
    "development": DevelopmentConfig,
    "testing": TestingConfig,
    "production": ProductionConfig,
    "default": DevelopmentConfig,
}

2.3 工厂模式 + 配置类结合

python
# app/__init__.py
import os
from flask import Flask
from config import config_map


def create_app(config_name: str | None = None) -> Flask:
    """应用工厂函数"""

    # 未指定时从环境变量读取,默认为 development
    if config_name is None:
        config_name = os.environ.get("FLASK_ENV", "development")

    app: Flask = Flask(__name__)

    # 加载对应配置类
    config_class: type[config_map["default"]] = config_map.get(
        config_name, config_map["default"]
    )
    app.config.from_object(config_class)

    # 执行配置类中的初始化钩子
    config_class.init_app(app)

    # 注册蓝图
    from app.main import main as main_blueprint
    from app.auth import auth as auth_blueprint

    app.register_blueprint(main_blueprint)
    app.register_blueprint(auth_blueprint, url_prefix="/auth")

    # 注册扩展
    from app.extensions import db, migrate, login_manager

    db.init_app(app)
    migrate.init_app(app, db)
    login_manager.init_app(app)

    return app
python
# run.py
from app import create_app

# 根据环境变量自动选择配置
app = create_app()

if __name__ == "__main__":
    app.run()
bash
# 切换环境
export FLASK_ENV=development && python run.py
export FLASK_ENV=production && python run.py

第三部分:L2 实践层

3.1 dotenv 文件(Flask 3.x 原生支持)

Flask 3.x 内置对 .env.flaskenv 文件的支持,通过 python-dotenv 加载:

dotenv 加载流程:
┌─────────────────────────────────────────────────────────────┐
│          Flask 启动时环境变量加载顺序                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 系统环境变量(优先级最高)                                 │
│     ↓                                                       │
│  2. 项目根目录的 .env 文件                                   │
│     ↓                                                       │
│  3. 项目根目录的 .flaskenv 文件(仅 Flask 相关)             │
│     ↓                                                       │
│  4. 代码中的默认值(优先级最低)                               │
│                                                             │
│  注意:已存在的环境变量不会被 .env 文件覆盖                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
ini
# .env —— 开发环境敏感配置(不提交到 Git)
SECRET_KEY=your-super-secret-key-here
DATABASE_URL=postgresql://user:password@localhost/dev_db
REDIS_URL=redis://localhost:6379/0
MAIL_PASSWORD=email-app-password
FLASK_APP=run.py
FLASK_ENV=development
ini
# .flaskenv —— Flask CLI 相关配置(可以提交到 Git)
FLASK_APP=run.py
FLASK_ENV=development
FLASK_DEBUG=1
python
# app/__init__.py
import os
from flask import Flask
from dotenv import load_dotenv

# 显式加载 .env(Flask CLI 自动加载,但程序运行时需手动加载)
dotenv_path: str = os.path.join(os.path.dirname(__file__), "..", ".env")
load_dotenv(dotenv_path)

def create_app() -> Flask:
    app: Flask = Flask(__name__)

    # 从环境变量读取敏感配置
    app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY")
    app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL")

    return app

重要.env 文件必须添加到 .gitignore,防止敏感信息泄露。

3.2 instance folders(实例文件夹)

Flask 提供 instance folder 机制,用于存放部署时特定的配置文件:

instance folder 位置:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  相对路径模式(默认):                                       │
│  myapp/                                                      │
│  ├── app/                                                    │
│  │   └── __init__.py  ← app.instance_path 指向 instance/    │
│  └── instance/                                               │
│      └── config.py    ← 部署时创建的实例配置                  │
│                                                             │
│  绝对路径模式:                                               │
│  myapp/                                                      │
│  ├── app/                                                    │
│  │   └── __init__.py  ← instance_relative_config=True       │
│  └── /var/www/myapp-instance/                                │
│      └── config.py                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
python
# app/__init__.py
from flask import Flask

# instance_relative_config=True 使相对路径指向 instance/ 目录
app: Flask = Flask(__name__, instance_relative_config=True)

# 加载默认配置
app.config.from_object("config.DefaultConfig")

# 尝试从 instance 文件夹加载(不存在时不报错)
app.config.from_pyfile("config.py", silent=True)
python
# instance/config.py —— 部署时手动创建
SECRET_KEY = "production-secret-from-instance-folder"
SQLALCHEMY_DATABASE_URI = "postgresql://user:pass@localhost/prod_db"

3.3 环境变量 vs 配置文件 vs 代码

配置方式选择指南:
┌──────────────────────┬──────────────┬──────────────┬──────────────┐
│       配置方式        │   适用场景    │   优点        │    缺点      │
├──────────────────────┼──────────────┼──────────────┼──────────────┤
│ 环境变量              │ 敏感信息     │ 不入库、安全  │ 管理复杂     │
│ (.env / 系统变量)     │ 部署差异     │ 平台通用      │ IDE 不支持   │
├──────────────────────┼──────────────┼──────────────┼──────────────┤
│ 配置类/文件           │ 结构配置     │ 版本可控      │ 不能存密钥   │
│ (config.py)           │ 默认值       │ IDE 支持好    │ 需区分环境   │
├──────────────────────┼──────────────┼──────────────┼──────────────┤
│ instance/config.py   │ 部署时配置   │ 代码分离      │ 部署步骤多   │
│                      │ 服务器特定    │ 不入库        │ 难以自动化   │
├──────────────────────┼──────────────┼──────────────┼──────────────┤
│ 密钥管理服务          │ 生产密钥     │ 最安全        │ 依赖外部服务 │
│ (Vault/SecretsMgr)   │ 多实例部署    │ 集中管理      │ 增加复杂度   │
└──────────────────────┴──────────────┴──────────────┴──────────────┘

3.4 最佳实践

推荐配置架构:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  第 1 层:config.py(提交到 Git)                             │
│  ├── 配置类定义(开发/测试/生产)                             │
│  ├── 非敏感默认值                                            │
│  └── 配置选择逻辑                                            │
│       ↓                                                      │
│  第 2 层:.env(不提交,.gitignore)                          │
│  ├── 敏感配置(SECRET_KEY、数据库密码)                       │
│  ├── 环境特定变量                                            │
│  └── 开发便利变量(FLASK_APP、FLASK_DEBUG)                   │
│       ↓                                                      │
│  第 3 层:instance/config.py(部署时手动创建)                 │
│  ├── 服务器特定配置                                           │
│  ├── 生产覆盖值                                               │
│  └── 可选,与环境变量互补                                     │
│                                                             │
│  配置加载优先级:                                             │
│  环境变量 > .env > instance/config.py > config.py 默认值     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
做法原因示例
敏感信息放环境变量避免提交到版本控制os.environ.get("SECRET_KEY")
配置类存非敏感默认值版本可控,文档化SQLALCHEMY_TRACK_MODIFICATIONS = False
.env 加入 .gitignore防止密钥泄露.env 写入 .gitignore
提供 .env.example开发者知道需要哪些变量提交模板,不提交真实值
生产使用密钥管理服务集中管理、轮换、审计AWS Secrets Manager, HashiCorp Vault

3.5 常见陷阱

陷阱一:密钥泄露

python
# ❌ 错误:硬编码密钥到代码
app.config["SECRET_KEY"] = "super-secret-key-123"
app.config["DATABASE_URI"] = "mysql://root:password123@localhost/db"

# ✅ 正确:从环境变量读取
app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY")
app.config["DATABASE_URI"] = os.environ.get("DATABASE_URL")

陷阱二:环境混淆

python
# ❌ 错误:没有环境区分,生产用 DEBUG=True
app.config["DEBUG"] = True  # 生产环境会暴露堆栈信息

# ✅ 正确:通过配置类隔离
# config.py 中 ProductionConfig.DEBUG = False

陷阱三:配置加载顺序错误

python
# ❌ 错误:from_pyfile 在 from_object 之前,默认值覆盖了实例配置
app.config.from_pyfile("config.py", silent=True)
app.config.from_object(DevelopmentConfig)  # 会覆盖 from_pyfile 的值

# ✅ 正确:先加载默认,再用实例配置覆盖
app.config.from_object(DevelopmentConfig)
app.config.from_pyfile("config.py", silent=True)  # 后加载的优先级高

陷阱四:.env 文件提交到 Git

gitignore
# .gitignore
.env              # 包含真实密钥
.env.local        # 本地覆盖
*.pyc
__pycache__/
instance/         # 实例文件夹也建议忽略
bash
# 提供模板供团队使用
# .env.example
SECRET_KEY=change-me-to-random-string
DATABASE_URL=sqlite:///dev.db
MAIL_PASSWORD=your-email-app-password

第四部分:L3 专家层

4.1 Flask 内置配置值详解

Flask 内置配置分类:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  安全类:                                                    │
│  ├── SECRET_KEY          Session/CSRF 签名密钥               │
│  ├── SESSION_COOKIE_*    Session Cookie 各项属性              │
│  └── MAX_CONTENT_LENGTH  最大请求体大小                      │
│                                                             │
│  应用类:                                                    │
│  ├── DEBUG               调试模式(启用交互调试器)           │
│  ├── TESTING             测试模式(异常不传播)               │
│  └── ENV / FLASK_ENV     环境标识                            │
│                                                             │
│  服务器类:                                                   │
│  ├── SERVER_NAME         服务器名称和端口                    │
│  ├── PREFERRED_URL_SCHEME URL 生成时使用的协议               │
│  └── PERMANENT_SESSION_LIFETIME Session 有效期              │
│                                                             │
│  模板/静态文件:                                              │
│  ├── TEMPLATE_FOLDER     模板目录路径                        │
│  ├── STATIC_FOLDER       静态文件目录路径                    │
│  └── STATIC_URL_PATH     静态文件 URL 前缀                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

SECRET_KEY

python
# 生成安全的随机密钥
# app/key_gen.py
import secrets

# 生成 32 字节(256 位)的随机密钥
secret_key: str = secrets.token_hex(32)
print(secret_key)
# 输出示例:a3f2c8d1e9b745f6a0c2d4e8b1f3a5c7d9e0b2a4f6c8d0e2b4a6c8d0e2b4a6c8
代码片段含义
secrets.token_hex(32)生成 64 字符十六进制随机字符串
secrets.token_urlsafe(32)生成 URL 安全的 Base64 字符串
os.urandom(32)生成原始字节,适合底层使用

警告:不要使用 random 模块生成密钥,它不是密码学安全的。

配置项默认值说明
SESSION_COOKIE_NAME"session"Cookie 名称
SESSION_COOKIE_DOMAINNoneCookie 域名,None 为当前域名
SESSION_COOKIE_PATHNoneCookie 路径,None 为应用根路径
SESSION_COOKIE_HTTPONLYTrue阻止 JavaScript 访问(防 XSS)
SESSION_COOKIE_SECUREFalse仅 HTTPS 传输(生产应设为 True
SESSION_COOKIE_SAMESITENone防 CSRF,推荐设为 "Lax""Strict"
PERMANENT_SESSION_LIFETIMEtimedelta(days=31)永久 Session 过期时间
python
# app/config_security.py
from datetime import timedelta
from flask import Flask

app: Flask = Flask(__name__)

# 生产级 Session Cookie 配置
app.config["SESSION_COOKIE_HTTPONLY"] = True
app.config["SESSION_COOKIE_SECURE"] = True       # 仅 HTTPS
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"    # 防 CSRF
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(hours=2)

MAX_CONTENT_LENGTH

python
# 限制上传文件大小
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024  # 16MB

# 超过限制时,Flask 自动返回 413 Request Entity Too Large
# 无需手动检查

4.2 配置加载优先级链

配置加载优先级(从高到低):
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  1. 运行时直接赋值(最高优先级)                       │    │
│  │     app.config["KEY"] = "override-value"             │    │
│  └─────────────────────────────────────────────────────┘    │
│                        ↓                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  2. from_envvar()                                    │    │
│  │     app.config.from_envvar("APP_CONFIG_FILE")        │    │
│  └─────────────────────────────────────────────────────┘    │
│                        ↓                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  3. from_pyfile()                                   │    │
│  │     app.config.from_pyfile("instance/config.py")     │    │
│  └─────────────────────────────────────────────────────┘    │
│                        ↓                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  4. from_object()                                   │    │
│  │     app.config.from_object(ProductionConfig)         │    │
│  └─────────────────────────────────────────────────────┘    │
│                        ↓                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  5. Flask 内置默认值(最低优先级)                    │    │
│  │     app 创建时自动加载                                │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  原则:后加载的覆盖先加载的                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
python
# app/config_priority_demo.py
from flask import Flask

app: Flask = Flask(__name__, instance_relative_config=True)

# 加载顺序演示
# 第 1 步:Flask 内置默认值(DEBUG=False, SECRET_KEY=None 等)

# 第 2 步:从配置类加载默认值
app.config.from_object("config.DefaultConfig")

# 第 3 步:从实例文件夹加载(覆盖第 2 步)
app.config.from_pyfile("config.py", silent=True)

# 第 4 步:从环境变量加载(覆盖第 3 步)
app.config.from_envvar("APP_CONFIG_FILE", silent=True)

# 第 5 步:运行时直接赋值(最高优先级)
app.config["SPECIAL_FLAG"] = True

4.3 敏感配置的安全管理

生产环境密钥管理架构:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  方案一:环境变量(最常用)                                   │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  部署平台:Docker / K8s / Heroku / AWS                │    │
│  │  做法:在部署平台界面设置环境变量                     │    │
│  │  代码:os.environ.get("SECRET_KEY")                   │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  方案二:密钥管理服务(企业级)                               │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  AWS Secrets Manager / HashiCorp Vault /              │    │
│  │  Azure Key Vault / GCP Secret Manager                 │    │
│  │  做法:应用启动时从服务拉取密钥                       │    │
│  │  优势:自动轮换、审计日志、细粒度权限                 │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  方案三:加密配置文件                                       │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  做法:配置文件加密存储,启动时解密                   │    │
│  │  工具:sops + AWS KMS / GPG                          │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

HashiCorp Vault 集成示例

python
# app/vault_config.py
import os
import hvac


def load_secrets_from_vault(vault_addr: str, vault_token: str) -> dict[str, str]:
    """从 Vault 加载密钥"""

    client: hvac.Client = hvac.Client(url=vault_addr, token=vault_token)

    # 读取密钥(Vault KV v2 引擎)
    secret_data: dict[str, str] = client.secrets.kv.v2.read_secret_version(
        path="flask-app",
        mount_point="secret",
    )["data"]["data"]

    return secret_data


def apply_vault_config(app) -> None:
    """将 Vault 密钥应用到 Flask 配置"""

    vault_addr: str = os.environ.get("VAULT_ADDR", "https://vault.company.com")
    vault_token: str = os.environ.get("VAULT_TOKEN")

    if vault_token:
        secrets: dict[str, str] = load_secrets_from_vault(vault_addr, vault_token)
        app.config["SECRET_KEY"] = secrets.get("SECRET_KEY")
        app.config["SQLALCHEMY_DATABASE_URI"] = secrets.get("DATABASE_URL")
        app.config["MAIL_PASSWORD"] = secrets.get("MAIL_PASSWORD")

AWS Secrets Manager 集成示例

python
# app/aws_config.py
import json
import os
import boto3
from botocore.exceptions import ClientError


def get_secret(secret_name: str, region: str = "us-east-1") -> dict[str, str]:
    """从 AWS Secrets Manager 获取密钥"""

    client = boto3.client("secretsmanager", region_name=region)

    try:
        response = client.get_secret_value(SecretId=secret_name)
        secret_string: str = response["SecretString"]
        return json.loads(secret_string)
    except ClientError as e:
        # 生产环境建议失败快速(fail-fast)
        raise RuntimeError(f"无法加载密钥 {secret_name}: {e}") from e


def apply_aws_secrets(app) -> None:
    """将 AWS 密钥应用到 Flask 配置"""

    secret_name: str = os.environ.get("AWS_SECRET_NAME", "flask-app/prod")
    secrets: dict[str, str] = get_secret(secret_name)

    app.config["SECRET_KEY"] = secrets["SECRET_KEY"]
    app.config["SQLALCHEMY_DATABASE_URI"] = secrets["DATABASE_URL"]

4.4 知识关联图

配置管理知识关联:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│                    ┌───────────────────┐                    │
│                    │   环境变量管理     │                    │
│                    │   (os.environ)    │                    │
│                    └─────────┬─────────┘                    │
│                              │                              │
│              ┌───────────────┼───────────────┐              │
│              ↓               ↓               ↓              │
│  ┌──────────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│  │   python-dotenv  │ │  app.config   │ │  instance folder │ │
│  │   (.env 文件)    │ │  (配置容器)    │ │  (部署时配置)    │ │
│  └────────┬─────────┘ └──────┬───────┘ └────────┬─────────┘ │
│           │                  │                   │          │
│           └──────────┬───────┘                   │          │
│                      ↓                           ↓          │
│            ┌───────────────────┐       ┌──────────────────┐ │
│            │  配置类模式       │       │  密钥管理服务     │ │
│            │  (Dev/Test/Prod)  │       │  (Vault/AWS)     │ │
│            └─────────┬─────────┘       └────────┬─────────┘ │
│                      │                           │          │
│                      ↓                           ↓          │
│            ┌───────────────────┐       ┌──────────────────┐ │
│            │  应用工厂模式     │       │  安全最佳实践     │ │
│            │  (create_app)     │       │  (HTTPS/Cookie)  │ │
│            └───────────────────┘       └──────────────────┘ │
│                                                             │
│  前置知识:字典操作、环境变量、Python 模块导入               │
│  进阶扩展:CI/CD 配置注入、基础设施即代码 (Terraform)        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

本章总结

配置管理全景:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  L1 基础:                                                   │
│  ├── app.config 是类字典对象,管理 Flask 所有配置            │
│  ├── 直接赋值:app.config['KEY'] = value                    │
│  ├── from_object():从 Python 类/模块加载                   │
│  └── from_envvar():从环境变量指定的文件加载                 │
│                                                             │
│  L1 多环境:                                                 │
│  ├── 配置类模式:DevelopmentConfig / TestingConfig / Prod    │
│  ├── 工厂模式 + 配置类:create_app(config_name)             │
│  └── config_map 字典实现动态选择                             │
│                                                             │
│  L2 实践:                                                   │
│  ├── .env 文件(Flask 3.x 原生支持)                        │
│  ├── instance/config.py(部署时特定配置)                    │
│  ├── 环境变量存密钥,配置类存默认值                          │
│  └── 避免:硬编码密钥、DEBUG=True 上生产、提交 .env          │
│                                                             │
│  L3 专家:                                                   │
│  ├── 配置加载优先级:运行时 > from_envvar > from_pyfile     │
│  │                    > from_object > 内置默认              │
│  ├── SESSION_COOKIE_SECURE=True, SAMESITE=Lax 生产必设      │
│  └── 密钥管理:Vault / AWS Secrets Manager / 加密文件       │
│                                                             │
│  核心原则:                                                  │
│  代码存结构,环境存差异,密钥存服务                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘