Python项目的私密信息处理

1. 情景

src 这种项目结构下,如果业务逻辑需要一个私密的token,应该如何配置最好(考虑 git上传)

2. 做法

典型做法是:代码里永远不直接写 token,而是通过「环境变量/本地私密文件(不进 Git)」加载,再用一个示例配置文件告诉协作者需要哪些字段。下面给出一个可复制的方案。

3. 示例(适用于 src/ 布局)

3.1 项目结构(关键文件)

my_project/
├── .gitignore
├── .env.example # 示例环境变量文件(提交)
├── .env # 真实密钥文件(不提交)
├── pyproject.toml
├── src/
│ └── my_package/
│ └── settings.py # 统一读取配置的入口
└── tests/

3.2 .gitignore

.gitignore内容(确保真实密钥不进仓库)

# .env 文件永不上传
.env
.env.*
!.env.example

3.3 .env.example

.env.example(提交,说明需要哪些变量)

# Copy to .env and fill in real values
MYAPP_API_TOKEN=
MYAPP_DB_URL=

3.4 .env

.env(本地/服务器放真实值,不提交)

dotenv复制编辑MYAPP_API_TOKEN=sk-xxxxxx
MYAPP_DB_URL=postgresql://user:pwd@host/db

3.5. 代码里读取

以 Pydantic Settings / python-dotenv 为例

安装依赖:

poetry add pydantic-settings python-dotenv
# 或者:poetry add pydantic-settings (不装dotenv也行,用Pydantic自带读取)

src/my_package/settings.py

python复制编辑from pydantic_settings import BaseSettings
from pydantic import SecretStr

class Settings(BaseSettings):
    api_token: SecretStr
    db_url: str | None = None

    class Config:
        env_file = ".env"
        env_prefix = "MYAPP_"   # 环境变量前缀,可选

settings = Settings()

业务代码使用:

from my_package.settings import settings

def call_api():
token = settings.api_token.get_secret_value()
...

优点:

.env 放在项目根,不进 Git;

.env.example 告诉同事/CI 要准备哪些变量;

Pydantic 会验证必填项、类型安全;

SecretStr 防止日志中直接打印出 Token。

4. 和直接用一个py文件硬编码对比

差别本质上在于安全性、可维护性、可迁移/部署性和团队协作成本。直接在 settings.py 里写 my_token = 'xxx' 看似简单,但一旦项目稍微复杂、多人协作或要部署到不同环境,就会出现一堆问题。

4.1 直接硬编码 vs. 配置/环境变量方案对比

维度硬编码在 settings.py.env / 环境变量 + 统一读取
安全性极易被误传到 Git、PR、日志;删了也留在历史中.gitignore 掉,CI/CD 使用 Secret Store,泄露概率低
密钥轮换要改代码、发新版本、重新部署改环境变量或 .env 即可,代码不动
多环境(dev/staging/prod)每个环境得改代码或分支环境变量天然支持同字段不同值
团队协作新人拿到代码却没密钥?写死就得给他仓库里那份…….env.example 告诉 TA 需要哪些值,自己填
测试/CI单元测试要 mock/改源码测试里注入临时 env,干净可控
合规与审计一旦泄漏需全仓库清理(git filter-repo)中心化管理,审计容易
AI 辅助写码AI 可能把真实 token 复制到别的地方AI 改代码也改不了你的环境变量值

4.2 常见“踩坑”例子(硬编码场景)

不小心把私有仓库转公开,密钥瞬间泄漏。

  • 合作者 fork 后你撤回不了他的副本。
  • 需要换 token,所有人都得更新代码再重新部署。
  • 本地与服务器、Docker、CI 环境的 token 不同——只能到处改源代码。
  • 日志或异常栈里打印了整个 settings 对象,token 跟着暴露。

4.4 推荐的“配置加载”方式做了什么?

  • 统一入口(例如 settings.py 用 Pydantic Settings / dotenv):
    • 负责从 .env / 环境变量 / 命令行 / 配置文件读取;
    • 负责校验必填项(不填就报错),给默认值;
    • 负责类型转换(比如把字符串的“true”“10.5”转为 bool/float);
    • 避免每个地方都 os.getenv("XXX") 乱写一通。
  • 代码与秘密解耦:业务逻辑只关心 settings.api_token,不关心它从哪来。

4.5 什么时候可以“偷懒”硬编码?

  • 一次性的小脚本、彻底个人用途、永不上传也不跟别人共享。
  • 临时 demo,马上要删。

但即使如此,也建议用环境变量习惯,养成模式后不会觉得麻烦。

发表评论