fix(security): JWT密钥与默认账号改用环境变量,清理泄露凭据

- JWT_SECRET_KEY 改从环境变量读取,未设置时使用临时随机密钥并告警
- 默认管理员账号密码改用 ADMIN_USERNAME/ADMIN_PASSWORD 环境变量配置
- 移除登录页及启动/部署脚本中的默认密码明文提示
- docker-compose.yml 添加环境变量支持(从 .env 文件读取)
- 新增 .env.example 配置文档,.gitignore 添加 .env 规则
- git rm --cached 清理已追踪的 data/accounts.json 和 data/cookies/敏感文件
This commit is contained in:
yml2213
2026-06-23 06:42:37 +08:00
parent 2ccc2a9336
commit db384701a9
10 changed files with 41 additions and 27 deletions
+11
View File
@@ -0,0 +1,11 @@
# ===== 环境变量配置 =====
# 复制此文件为 .env 并修改相应值
# Docker 部署时 docker-compose.yml 会自动读取 .env 文件
# JWT 密钥(生产环境必须设置!)
# 生成方式: python -c "import secrets; print(secrets.token_urlsafe(32))"
JWT_SECRET_KEY=
# 默认管理员账号(仅首次启动建库时生效)
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
+3
View File
@@ -3,6 +3,9 @@ __pycache__/
*/__pycache__ */__pycache__
*.pyc *.pyc
# 环境变量(含敏感信息,勿提交)
.env
# 运行时数据 # 运行时数据
data/ data/
logs/ logs/
-12
View File
@@ -1,12 +0,0 @@
{
"accounts": [
{
"username": "your_username",
"password": "your_password",
"email": "your_email@bdhg.xyz",
"email_password": "your_email_password",
"email_imap_server": "mail.bdhg.xyz",
"email_imap_port": 993
}
]
}
-5
View File
@@ -1,5 +0,0 @@
{
"username": "用户9006787894",
"cookie": "dy_accounts_main=; dy_auth=e105BJzhxVWQ0tJKUXiiqN8ky11wgfj7ClW%2F8qo%2B1MPv8%2BDGHBq8VnuaJ1MWnm4EZqgVfLkM6PETWtGpLAHvadcMxCn9HWvTfT8%2BSKZR2dmsvReubJmeqBo; wan_auth37wan=4dc825c71d00LrMvodf%2B7ZrhnyMcsREgwQlgd7Ec06k5%2Bm%2FM0tdv0AJJ8gpoNoinSoPHS3vbJO8iK1vqiXfF2hTOLQqJOW3Wzs19DWLwpJpQgV8J%2BF0; LTP0=eyJhbGciOiJtZDUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOlsicGFzc3BvcnQiXSwiY3QiOjAsImN0aW1lIjoxNzgyMDU0MDA0LCJleHAiOjE3OTc4MjIwMTYsImtleSI6ImR5LWp3dC1tZDUiLCJsdGsiOiJhZDMxOTliZGRhYmM3ZWRmIiwibHRraWQiOjgwNTYxMzgyLCJzdWIiOiJsdCIsInVpZCI6OTg2Mzg1Mjk2fQ.Y2M0YTQ4YjQwZGYwZWI4YWZiZTllNmVjODNiZmJkMGE; last_login_way=nickname; PHPSESSID=ccr09aa2letju5662gdh5aih62; acf_auth=1a34kGp6rHjazzRy4fVzKXX11qCjC9RuQASDGOEfkRbLjsI3A5PzSCXawo5hZvO4g47LF9ruapNiMpf%2BE3u4TplWPMkW4c4eI0pyG05a%2F9dKr%2BOGM0hAEXk; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM4NTI5NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MDU2MTM4Miwic3RrIjoiNjFhODczZjUwZTg3M2Y5MCIsImV4cCI6MTc4MjY1ODgwNCwiaWF0IjoxNzgyMDU0MDA0LCJrZXkiOiJkeS1qd3QtbWQ1In0.OTFlMjIxOGU4MjZkMjcwMWM4NjQ1NTcwNjNmZWIzMDA; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM4NTI5NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MDU2MTM4Miwic3RrIjoiNjFhODczZjUwZTg3M2Y5MCIsImV4cCI6MTc4MjY1ODgwNCwiaWF0IjoxNzgyMDU0MDA0LCJrZXkiOiJkeS1qd3QtbWQ1In0.N2E0NmEyOWVlZDE1NTE4NDg0MTFlMmM5OGY1ZGQzZTk; acf_uid=986385296; acf_username=986385296; acf_nickname=%E7%94%A8%E6%88%B79006787894; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F25_; acf_ct=0; acf_ltkid=80561382; acf_biz=1; acf_stk=61a873f50e873f90; acf_devid=d53498d383f449f4c4ea28c4165aa2b4",
"timestamp": 1782054016
}
+1 -1
View File
@@ -54,7 +54,7 @@ for i in $(seq 1 10); do
echo " ✅ 部署成功!" echo " ✅ 部署成功!"
echo "==============================" echo "=============================="
echo " 访问地址: http://localhost:8000" echo " 访问地址: http://localhost:8000"
echo " 默认账号: admin / admin123" echo " 默认账号见环境变量 ADMIN_USERNAME/ADMIN_PASSWORD(未设置则为 admin/admin123"
echo "" echo ""
echo " 查看日志: $COMPOSE logs -f" echo " 查看日志: $COMPOSE logs -f"
echo " 停止服务: $COMPOSE down" echo " 停止服务: $COMPOSE down"
+5
View File
@@ -11,6 +11,11 @@ services:
- ./logs:/app/logs - ./logs:/app/logs
environment: environment:
- TZ=Asia/Shanghai - TZ=Asia/Shanghai
# JWT 密钥(生产环境务必修改,可用 python -c "import secrets; print(secrets.token_urlsafe(32))" 生成)
- JWT_SECRET_KEY=${JWT_SECRET_KEY:-}
# 默认管理员账号密码(仅首次启动建库时生效)
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123}
healthcheck: healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"] test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
interval: 30s interval: 30s
+1 -1
View File
@@ -28,7 +28,7 @@ echo " 斗鱼批量登录 - Web 后台"
echo "==============================" echo "=============================="
echo " 后端: http://localhost:$BACKEND_PORT" echo " 后端: http://localhost:$BACKEND_PORT"
echo " 前端: http://localhost:$FRONTEND_PORT" echo " 前端: http://localhost:$FRONTEND_PORT"
echo " 默认账号: admin / admin123" echo " 默认账号见环境变量 ADMIN_USERNAME/ADMIN_PASSWORD(未设置则为 admin/admin123"
echo " 按 Ctrl+C 停止所有服务" echo " 按 Ctrl+C 停止所有服务"
echo "==============================" echo "=============================="
echo "" echo ""
+7 -3
View File
@@ -1,5 +1,6 @@
"""数据库引擎与会话管理""" """数据库引擎与会话管理"""
import os
from pathlib import Path from pathlib import Path
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base from sqlalchemy.orm import sessionmaker, declarative_base
@@ -73,16 +74,19 @@ def _migrate():
def _seed(): def _seed():
"""写入默认超管账号和角色。""" """写入默认超管账号和角色(账号密码通过环境变量配置)"""
from .models import User from .models import User
from .security import hash_password from .security import hash_password
admin_username = os.getenv("ADMIN_USERNAME", "admin")
admin_password = os.getenv("ADMIN_PASSWORD", "admin123")
db = SessionLocal() db = SessionLocal()
try: try:
if not db.query(User).first(): if not db.query(User).first():
admin = User( admin = User(
username="admin", username=admin_username,
password_hash=hash_password("admin123"), password_hash=hash_password(admin_password),
role="super_admin", role="super_admin",
is_active=True, is_active=True,
remark="默认超级管理员", remark="默认超级管理员",
+13 -2
View File
@@ -1,13 +1,24 @@
"""安全模块:密码哈希 + JWT""" """安全模块:密码哈希 + JWT"""
import os
import secrets
import warnings
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Optional from typing import Optional
import bcrypt import bcrypt
from jose import jwt, JWTError from jose import jwt, JWTError
SECRET_KEY = "douyu-web-secret-change-in-production" # JWT 密钥:生产环境必须通过环境变量 JWT_SECRET_KEY 设置
SECRET_KEY = os.getenv("JWT_SECRET_KEY")
if not SECRET_KEY:
warnings.warn(
"未设置 JWT_SECRET_KEY 环境变量,使用临时随机密钥(重启后所有 Token 将失效)。"
"生产环境请务必设置 JWT_SECRET_KEY 环境变量。",
stacklevel=2,
)
SECRET_KEY = secrets.token_urlsafe(32)
ALGORITHM = "HS256" ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_HOURS = 24 ACCESS_TOKEN_EXPIRE_HOURS = int(os.getenv("ACCESS_TOKEN_EXPIRE_HOURS", "24"))
def hash_password(password: str) -> str: def hash_password(password: str) -> str:
-3
View File
@@ -61,9 +61,6 @@ export default function LoginPage({ onLogin }: { onLogin?: () => void }) {
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
<div style={{ textAlign: 'center', fontSize: 12 }}>
默认管理员: admin / admin123
</div>
</Card> </Card>
</div> </div>
); );