环境变量配置#
环境变量列表#
后端环境变量#
| 变量名 | 说明 | 默认值 | 必需 |
|---|---|---|---|
DB_HOST | 数据库主机 | localhost | ✅ |
DB_PORT | 数据库端口 | 5432 | ✅ |
DB_NAME | 数据库名称 | waterball_academy | ✅ |
DB_USERNAME | 数据库用户名 | postgres | ✅ |
DB_PASSWORD | 数据库密码 | postgres | ✅ |
JWT_SECRET | JWT 签名密钥(>=32字符) | - | ✅ |
CORS_ORIGINS | 允许的跨域来源 | http://localhost:3000 | ✅ |
SERVER_PORT | 服务器端口 | 8080 | ❌ |
GOOGLE_CLIENT_ID | Google OAuth Client ID | - | ❌ |
GOOGLE_CLIENT_SECRET | Google OAuth Secret | - | ❌ |
前端环境变量#
| 变量名 | 说明 | 默认值 | 必需 |
|---|---|---|---|
NEXT_PUBLIC_API_URL | 后端 API 地址 | http://localhost:8080 | ✅ |
NEXT_PUBLIC_GOOGLE_CLIENT_ID | Google OAuth Client ID | - | ❌ |
配置文件#
开发环境(docker-compose.yml)#
version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: waterball_academy
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
backend:
build: ./backend
ports:
- "8080:8080"
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: waterball_academy
DB_USERNAME: postgres
DB_PASSWORD: postgres
JWT_SECRET: "your-dev-jwt-secret-min-32-chars"
CORS_ORIGINS: "http://localhost:3000,http://localhost:3001"
GOOGLE_CLIENT_ID: "${GOOGLE_CLIENT_ID:-}"
GOOGLE_CLIENT_SECRET: "${GOOGLE_CLIENT_SECRET:-}"
depends_on:
- postgres
frontend:
build: ./frontend
ports:
- "3001:3000"
environment:
NEXT_PUBLIC_API_URL: http://localhost:8080
NEXT_PUBLIC_GOOGLE_CLIENT_ID: "${GOOGLE_CLIENT_ID:-}"
depends_on:
- backend
volumes:
postgres_data:生产环境(docker-compose.prod.yml)#
version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
ports:
- "8080:8080"
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: ${DB_NAME}
DB_USERNAME: ${DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD}
JWT_SECRET: ${JWT_SECRET}
CORS_ORIGINS: ${CORS_ORIGINS}
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
depends_on:
- postgres
restart: unless-stopped
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
NEXT_PUBLIC_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
ports:
- "3001:3000"
depends_on:
- backend
restart: unless-stopped
volumes:
postgres_data:.env 文件(生产环境)#
# .env - 不要提交到 Git!
# 数据库配置
DB_NAME=waterball_academy
DB_USERNAME=postgres
DB_PASSWORD=your_secure_password_here
# JWT 配置(必须 >= 32 字符)
JWT_SECRET=your_very_long_and_secure_secret_key_at_least_32_characters
# CORS 配置(多个域名用逗号分隔)
CORS_ORIGINS=https://waterball.yorukaru.com,https://www.waterball.yorukaru.com
# 前端 API URL
NEXT_PUBLIC_API_URL=https://waterball.yorukaru.com
# Google OAuth(可选)
GOOGLE_CLIENT_ID=656680013665-xxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxxxxxx本地开发配置#
后端(application.properties)#
# Database
spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:waterball_academy}
spring.datasource.username=${DB_USERNAME:postgres}
spring.datasource.password=${DB_PASSWORD:postgres}
# JPA/Hibernate
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
# JWT
jwt.secret=${JWT_SECRET}
jwt.expiration=86400000
# CORS
cors.allowed.origins=${CORS_ORIGINS:http://localhost:3000,http://localhost:3001}
# Google OAuth
google.client.id=${GOOGLE_CLIENT_ID:}
google.client.secret=${GOOGLE_CLIENT_SECRET:}
# Server
server.port=${SERVER_PORT:8080}前端(.env.local)#
# .env.local - 开发环境
NEXT_PUBLIC_API_URL=http://localhost:8080
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id安全最佳实践#
1. 敏感信息管理#
✅ 推荐做法:
# 使用 .env 文件
echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env
# 使用密钥管理服务
# - AWS Secrets Manager
# - Google Secret Manager
# - HashiCorp Vault❌ 禁止做法:
# 不要硬编码在代码中
JWT_SECRET=hardcoded_secret # ❌
# 不要提交到 Git
git add .env # ❌2. .gitignore 配置#
# 环境变量
.env
.env.local
.env.production
.env.development
# 敏感配置
application-secrets.properties3. JWT Secret 生成#
# 方法 1: OpenSSL
openssl rand -base64 32
# 方法 2: Python
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
# 方法 3: Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"4. 环境隔离#
开发环境:
- JWT_SECRET: 简单密钥(开发用)
- CORS_ORIGINS: localhost
- DB_PASSWORD: 简单密码
生产环境:
- JWT_SECRET: 强随机密钥
- CORS_ORIGINS: 实际域名
- DB_PASSWORD: 复杂密码Google OAuth 配置#
1. 创建 OAuth 2.0 凭据#
- 访问 Google Cloud Console
- 创建项目
- 启用 Google+ API
- 创建 OAuth 2.0 客户端 ID
授权重定向 URI:
http://localhost:3001 # 开发环境
https://waterball.yorukaru.com # 生产环境2. 获取凭据#
复制以下信息到 .env:
- Client ID
- Client Secret
3. 测试配置#
# 检查环境变量
echo $GOOGLE_CLIENT_ID
echo $GOOGLE_CLIENT_SECRET
# 启动应用测试 OAuth 登录
docker-compose up故障排查#
环境变量未生效#
检查步骤:
# 1. 查看容器环境变量
docker-compose exec backend env | grep JWT_SECRET
# 2. 重启容器
docker-compose down
docker-compose up -d
# 3. 检查 .env 文件编码(应为 UTF-8)
file -i .envJWT Secret 太短#
错误信息:
The specified key byte array is 128 bits which is not secure enough解决:
# 确保 >= 32 字符
JWT_SECRET=$(openssl rand -base64 32)CORS 错误#
检查 CORS_ORIGINS:
# 正确格式(无空格)
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
# 错误格式
CORS_ORIGINS=http://localhost:3000, http://localhost:3001 # ❌ 有空格CI/CD 集成#
GitHub Actions 示例#
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up environment
run: |
echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> .env
echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .env
echo "GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}" >> .env
- name: Deploy with docker-compose
run: docker-compose -f docker-compose.prod.yml up -d --build配置 GitHub Secrets:
- Settings → Secrets and variables → Actions
- 添加环境变量(JWT_SECRET, DB_PASSWORD 等)