Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .env.release.draft
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,16 @@ DEVICE_AUTH_VERIFICATION_URI=
# Leave both empty if you are not enabling GitHub login yet.
OAUTH2_GITHUB_CLIENT_ID=
OAUTH2_GITHUB_CLIENT_SECRET=

# SMTP configuration for password reset verification emails.
SPRING_MAIL_HOST=smtp.example.com
SPRING_MAIL_PORT=587
SPRING_MAIL_USERNAME=TODO_fill_smtp_username
SPRING_MAIL_PASSWORD=TODO_fill_smtp_password
SPRING_MAIL_SMTP_AUTH=true
SPRING_MAIL_SMTP_STARTTLS_ENABLE=true
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=false
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=
SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=noreply@example.com
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=SkillHub
13 changes: 13 additions & 0 deletions .env.release.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ DEVICE_AUTH_VERIFICATION_URI=
OAUTH2_GITHUB_CLIENT_ID=
OAUTH2_GITHUB_CLIENT_SECRET=

# SMTP configuration for password reset verification emails.
SPRING_MAIL_HOST=
SPRING_MAIL_PORT=587
SPRING_MAIL_USERNAME=
SPRING_MAIL_PASSWORD=
SPRING_MAIL_SMTP_AUTH=true
SPRING_MAIL_SMTP_STARTTLS_ENABLE=true
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=false
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=
SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=noreply@example.com
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=SkillHub

# Security scanner is enabled by default. Set to false to disable scanning.
SKILLHUB_SECURITY_SCANNER_ENABLED=true

Expand Down
11 changes: 11 additions & 0 deletions compose.release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ services:
BOOTSTRAP_ADMIN_EMAIL: ${BOOTSTRAP_ADMIN_EMAIL:-admin@skillhub.local}
OAUTH2_GITHUB_CLIENT_ID: ${OAUTH2_GITHUB_CLIENT_ID:-local-placeholder}
OAUTH2_GITHUB_CLIENT_SECRET: ${OAUTH2_GITHUB_CLIENT_SECRET:-local-placeholder}
SPRING_MAIL_HOST: ${SPRING_MAIL_HOST:-}
SPRING_MAIL_PORT: ${SPRING_MAIL_PORT:-25}
SPRING_MAIL_USERNAME: ${SPRING_MAIL_USERNAME:-}
SPRING_MAIL_PASSWORD: ${SPRING_MAIL_PASSWORD:-}
SPRING_MAIL_SMTP_AUTH: ${SPRING_MAIL_SMTP_AUTH:-false}
SPRING_MAIL_SMTP_STARTTLS_ENABLE: ${SPRING_MAIL_SMTP_STARTTLS_ENABLE:-false}
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE: ${SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE:-false}
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST: ${SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST:-}
SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY: ${SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY:-PT10M}
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS: ${SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS:-noreply@skillhub.local}
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME: ${SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME:-SkillHub}
volumes:
- skillhub_storage:/var/lib/skillhub/storage
depends_on:
Expand Down
1 change: 1 addition & 0 deletions docs/09-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ docker compose --env-file .env.release -f compose.release.yml up -d
- 外部对象存储通过 `SKILLHUB_STORAGE_S3_*` 注入
- 前端反代和运行时 API 地址通过 `SKILLHUB_API_UPSTREAM` / `SKILLHUB_WEB_API_BASE_URL` 注入
- 如果要开放真实登录,再补充 `OAUTH2_GITHUB_CLIENT_ID` / `OAUTH2_GITHUB_CLIENT_SECRET`
- 如果要启用密码重置验证码邮件,参见:`docs/19-smtp-password-reset-email-setup.md`

## 8 裸金属上线清单

Expand Down
317 changes: 317 additions & 0 deletions docs/19-smtp-password-reset-email-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
# SkillHub SMTP 邮箱配置指南(验证码邮件)

本文说明如何为 SkillHub 配置 SMTP,用于发送“密码重置验证码”邮件。

适用场景:
- 生产/预发布环境(`compose.release.yml` + `.env.release`)
- 本地联调环境(直接注入后端环境变量)

补充说明:
- SMTP 本质是邮件传输协议,不是单一厂商产品。
- 你可以使用企业邮箱、云邮箱或本地测试 SMTP 服务(例如 MailHog)作为 SMTP 服务端。

当前密码重置页面入口说明:
- 当前前端统一使用 `/reset-password` 页面。
- 该页面同时包含“发送验证码”和“提交新密码”两步,不再单独使用 `/forgot-password`。

## 1. 需要配置的环境变量

以下变量已被后端读取:

| 变量名 | 说明 | 示例 |
|---|---|---|
| `SPRING_MAIL_HOST` | SMTP 服务器地址 | `smtp.example.com` |
| `SPRING_MAIL_PORT` | SMTP 端口 | `465` |
| `SPRING_MAIL_USERNAME` | SMTP 用户名 | `noreply@example.com` |
| `SPRING_MAIL_PASSWORD` | SMTP 密码/授权码 | `xxxxxx` |
| `SPRING_MAIL_SMTP_AUTH` | 是否启用 SMTP AUTH | `true` |
| `SPRING_MAIL_SMTP_STARTTLS_ENABLE` | 是否启用 STARTTLS | `false` |
| `SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE` | 是否启用 SMTP SSL 直连 | `true` |
| `SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST` | SSL 信任主机(用于规避部分环境下证书链校验失败) | `smtp.mail.example` |
| `SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY` | 验证码有效期(ISO-8601 Duration) | `PT10M` |
| `SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS` | 发件人邮箱 | `noreply@example.com` |
| `SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME` | 发件人名称 | `SkillHub` |

说明:
- 当前文档统一按 `465 + SSL` 配置,不再展开 `587 + STARTTLS` 方案。
- 使用 `465` 时配置:`STARTTLS=false`、`SSL_ENABLE=true`。
- 若出现 `PKIX path building failed` / `SSLHandshakeException`,可尝试增加 `SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=<SMTP_HOST>`(本地联调常用)。
- 生产环境默认不建议配置 `SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST`,仅在证书链异常时临时启用。
- `SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY` 支持如 `PT5M`、`PT10M`、`PT30M`。

## 1.1 配置方案速查(推荐)

### A. 通用 SMTP 邮箱(本地直连真实邮箱)

```dotenv
SPRING_MAIL_HOST=smtp.mail.example
SPRING_MAIL_PORT=465
SPRING_MAIL_USERNAME=mailer@example.com
SPRING_MAIL_PASSWORD=your-smtp-app-password
SPRING_MAIL_SMTP_AUTH=true
SPRING_MAIL_SMTP_STARTTLS_ENABLE=false
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=true
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=smtp.mail.example
SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=mailer@example.com
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=your-from-name
```

本地 `export` 示例写法:

```bash
export SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=smtp.mail.example
export SPRING_MAIL_HOST=smtp.mail.example
export SPRING_MAIL_PORT=465
export SPRING_MAIL_USERNAME=mailer@example.com
export SPRING_MAIL_PASSWORD=your-smtp-app-password
export SPRING_MAIL_SMTP_AUTH=true
export SPRING_MAIL_SMTP_STARTTLS_ENABLE=false
export SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=true
export SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M
export SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=mailer@example.com
export SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=your-from-name
```

### B. MailHog(本地联调推荐)

```dotenv
SPRING_MAIL_HOST=127.0.0.1
SPRING_MAIL_PORT=1025
SPRING_MAIL_USERNAME=
SPRING_MAIL_PASSWORD=
SPRING_MAIL_SMTP_AUTH=false
SPRING_MAIL_SMTP_STARTTLS_ENABLE=false
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=false
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=noreply@skillhub.local
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=SkillHub
```

### C. 线上部署(465 端口示例)

```dotenv
SPRING_MAIL_HOST=smtp.mail.example
SPRING_MAIL_PORT=465
SPRING_MAIL_USERNAME=mailer@example.com
SPRING_MAIL_PASSWORD=your-smtp-app-password
SPRING_MAIL_SMTP_AUTH=true
SPRING_MAIL_SMTP_STARTTLS_ENABLE=false
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=true
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=smtp.mail.example
SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=mailer@example.com
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=your-from-name
```

## 2. 单机交付(Compose)配置步骤

1. 复制环境模板(若尚未创建):

```bash
cp .env.release.example .env.release
```

2. 编辑 `.env.release`,填写 SMTP 变量:

```dotenv
SPRING_MAIL_HOST=smtp.mail.example
SPRING_MAIL_PORT=465
SPRING_MAIL_USERNAME=mailer@example.com
SPRING_MAIL_PASSWORD=your-smtp-app-password
SPRING_MAIL_SMTP_AUTH=true
SPRING_MAIL_SMTP_STARTTLS_ENABLE=false
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=true
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=smtp.mail.example

SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=mailer@example.com
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=your-from-name
```

3. 重启后端容器使配置生效:

```bash
docker compose --env-file .env.release -f compose.release.yml up -d server
```

4. 查看后端日志确认启动正常:

```bash
docker compose --env-file .env.release -f compose.release.yml logs -f server
```

## 3. 本地开发配置与验证

### 3.1 一次性临时生效(推荐)

适合当前终端临时测试,重开终端后失效。

```bash
SPRING_MAIL_HOST=smtp.mail.example \
SPRING_MAIL_PORT=465 \
SPRING_MAIL_USERNAME=mailer@example.com \
SPRING_MAIL_PASSWORD=your-smtp-app-password \
SPRING_MAIL_SMTP_AUTH=true \
SPRING_MAIL_SMTP_STARTTLS_ENABLE=false \
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=true \
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=smtp.mail.example \
SKILLHUB_AUTH_PASSWORD_RESET_CODE_EXPIRY=PT10M \
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=mailer@example.com \
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=your-from-name \
make dev-server
```

### 3.2 长期生效(shell 配置)

如果你写到了 `~/.zshrc`,请注意:
- 必须 `source ~/.zshrc` 或重开终端后变量才会生效
- 需要在“同一个终端”启动 `make dev-server`

可先确认变量是否在当前 shell 中:

```bash
env | rg '^(SPRING_MAIL_|SKILLHUB_AUTH_PASSWORD_RESET_)'
```

### 3.3 推荐联调方式(MailHog)

如果你只是本地验证验证码链路,建议用 MailHog 作为本地 SMTP 服务:

1. 启动 MailHog:

```bash
docker run -d --name skillhub-mailhog \
-p 1025:1025 \
-p 8025:8025 \
mailhog/mailhog
```

2. 启动依赖服务(Postgres/Redis):

```bash
make dev
```

3. 启动后端时注入 SMTP 环境变量(示例):

```bash
SPRING_MAIL_HOST=127.0.0.1 \
SPRING_MAIL_PORT=1025 \
SPRING_MAIL_USERNAME= \
SPRING_MAIL_PASSWORD= \
SPRING_MAIL_SMTP_AUTH=false \
SPRING_MAIL_SMTP_STARTTLS_ENABLE=false \
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=false \
SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS=noreply@skillhub.local \
SKILLHUB_AUTH_PASSWORD_RESET_FROM_NAME=SkillHub \
make dev-server
```

4. 打开 MailHog Web UI 查看邮件:

```text
http://localhost:8025
```

5. 在 SkillHub 页面验证流程:
- 打开 `/reset-password`
- 输入邮箱并点击“发送验证码”
- 在 MailHog 中查看验证码邮件
- 输入邮箱 + 验证码 + 新密码完成重置

6. 也可使用接口做快速验证(示例):

```bash
curl -X POST http://localhost:8080/api/v1/auth/local/password-reset/request \
-H 'Content-Type: application/json' \
-d '{"email":"your-email@example.com"}'
```

## 4. 功能验证(验证码邮件)

### 4.1 用户自助找回

在 `/reset-password` 页面点击“发送验证码”后,系统会尝试发送验证码邮件。

说明:
- 为防止账号枚举,自助接口总是返回通用成功提示。
- 即使邮件发送失败,接口也可能返回成功;请结合后端日志确认实际发送结果。

### 4.2 管理员触发重置

管理员在用户管理页触发“重置密码”时,系统会强制发送验证码;
若 SMTP 发送失败,会返回错误(便于运维排障)。

## 5. 常见问题排查

### 5.1 认证失败(`535 Authentication failed`)

排查方向:
- 用户名/密码是否正确
- 邮箱服务是否要求“客户端授权码”而非登录密码
- 发件账号是否已开启 SMTP 服务

### 5.2 连接超时或拒绝连接

排查方向:
- 主机到 SMTP 服务端口 `465` 是否可达
- 安全组/防火墙是否放行出站连接
- SMTP 服务地址是否填写正确

### 5.3 本地明明配置了变量但不生效

排查方向:
- 是否只是编辑了 `~/.zshrc` 但没有 `source ~/.zshrc`
- 启动后端的终端是否与配置变量的终端是同一个
- `8080` 是否被旧进程占用,导致新进程没启动成功

可执行以下命令快速检查:

```bash
# 查看 8080 是否被旧进程占用
lsof -nP -iTCP:8080 -sTCP:LISTEN

# 查看当前 shell 是否有 SMTP 环境变量
env | rg '^(SPRING_MAIL_|SKILLHUB_AUTH_PASSWORD_RESET_)'
```

### 5.4 发件人被拒绝

排查方向:
- `SKILLHUB_AUTH_PASSWORD_RESET_FROM_ADDRESS` 是否与 SMTP 账号一致或已验证
- 邮箱服务是否限制别名发件

### 5.5 健康检查是否校验 SMTP

默认配置下,邮件健康检查关闭,不会因为 SMTP 不可达导致 `health` 失败。

若需要将 SMTP 连通性纳入健康检查,可设置:

```dotenv
MANAGEMENT_HEALTH_MAIL_ENABLED=true
```

### 5.6 SMTP 报 `PKIX path building failed`(证书链校验失败)

典型日志:
- `SSLHandshakeException`
- `unable to find valid certification path to requested target`

处理建议(本地联调):
- 增加:

```dotenv
SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=smtp.mail.example
```

- 然后重启后端,再触发一次“发送验证码”。

补充:
- 该配置用于指定信任主机,适合本地排障与联调。
- 生产环境默认不建议长期启用该配置,更推荐使用规范 CA 证书链或将企业 CA 导入 Java truststore。

## 6. 安全建议

- 不要把 SMTP 密码提交到仓库;仅写入受控的 `.env.release` 或密钥管理系统。
- 使用专用发信账号,避免使用个人邮箱主密码。
- 生产环境建议定期轮换 SMTP 授权码。
Loading
Loading